home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 December / PCWDEC07.iso / Software / Freeware / FlashGot 0.6.4 / components / flashgotService.js < prev   
Encoding:
Text File  |  2007-08-29  |  77.9 KB  |  2,563 lines

  1. /***** BEGIN LICENSE BLOCK *****
  2.  
  3.     FlashGot - a Firefox extension for external download managers integration
  4.     Copyright (C) 2004-2006 Giorgio Maone - g.maone@informaction.com
  5.  
  6.     This program is free software; you can redistribute it and/or modify
  7.     it under the terms of the GNU General Public License as published by
  8.     the Free Software Foundation; either version 2 of the License, or
  9.     (at your option) any later version.
  10.  
  11.     This program is distributed in the hope that it will be useful,
  12.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.     GNU General Public License for more details.
  15.  
  16.     You should have received a copy of the GNU General Public License
  17.     along with this program; if not, write to the Free Software
  18.     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.                              
  20. ***** END LICENSE BLOCK *****/
  21.  
  22. // *****************************************************************************
  23. // START DMS CLASSES
  24. // *****************************************************************************
  25.  
  26. const ASK_NEVER = [false, false, false];
  27.  
  28. // *** Base/Windows DMS ********************************************************
  29. function FlashGotDM(name) {
  30.   if(arguments.length > 0) {
  31.     this._init(name);
  32.   }
  33. }
  34.  
  35. FlashGotDM.init=function(service) {
  36.   FlashGotDM.cleanup();
  37.   FlashGotDM.dms = [];
  38.   FlashGotDM.dmtests = {};
  39.   FlashGotDM.executables = {};
  40.   FlashGotDM.initDMS(service); 
  41. };
  42.  
  43. FlashGotDM.cleanup=function() {
  44.   if(! ("executables" in FlashGotDM) ) return;
  45.   var name;
  46.   for(name in FlashGotDM.executables) {
  47.     var f=FlashGotDM.executables[name];
  48.     if(f instanceof Components.interfaces.nsIFile) {
  49.       try { f.remove(true); } catch(ex) {}
  50.     }
  51.   }
  52. };
  53.  
  54. FlashGotDM.prototype = {
  55.   _init: function(name) {
  56.     this.name = name;
  57.     const dms = FlashGotDM.dms;
  58.     var pos = dms.length;
  59.     if(name in dms) {
  60.       var other = dms[name];
  61.       for(var j = pos; j-- > 0;) {
  62.         if(dms[j] == other) {
  63.           pos=j;
  64.           break;
  65.         }
  66.       }
  67.     }
  68.     dms[name] = dms[pos] = this;
  69.   }
  70. ,
  71.   _service: null,
  72.   _cookieManager: null,
  73.   _exeFile: false,
  74.   _supported: null,
  75.   custom: false,
  76.   disabledLink: false,
  77.   disabledSel: false,
  78.   disabledAll: false,
  79.   exeName: "FlashGot.exe",
  80.   askPath: ASK_NEVER,
  81.   cookieSupport: true,
  82.   postSupport: false
  83. ,  
  84.   _codeName: null,
  85.   get codeName() {
  86.     return this._codeName || (this._codeName = this.name.replace(/\W/g,"_"));
  87.   },
  88.  
  89.   get shownInContextMenu() {
  90.     return this.service.getPref("flashgot.dmsopts." + this.codeName + ".shownInContextMenu", false);
  91.   },
  92.   set shownInContextMenu(b) {
  93.     this.service.setPref("flashgot.dmsopts." + this.codeName + ".shownInContextMenu", b);
  94.     return b;
  95.   }
  96. ,
  97.   get service() {
  98.     return this._service || (this._service =
  99.       Components.classes[SERVICE_CTRID].getService(Components.interfaces.nsISupports).wrappedJSObject);
  100.   }
  101. ,
  102.   get cookieManager() {
  103.     return this._cookieManager?this._cookieManager:this._cookieManager=
  104.       Components.classes["@mozilla.org/cookiemanager;1"
  105.         ].getService(Components.interfaces.nsICookieManager);
  106.   }
  107. ,
  108.   get exeFile() {
  109.     if(typeof(this._exeFile)=="object") return this._exeFile;
  110.     const exeName=this.exeName;
  111.     if(!exeName) return this._exeFile=null;
  112.     if(typeof(FlashGotDM.executables[exeName])=="object") {
  113.       return this._exeFile=FlashGotDM.executables[exeName];
  114.     }
  115.     try {
  116.       const exeFile=Components.classes["@mozilla.org/file/local;1"].createInstance(
  117.         Components.interfaces.nsILocalFile);
  118.       exeFile.initWithPath(this.service.globals.profDir.path);
  119.       exeFile.append(exeName);
  120.       if(exeFile.exists()) {
  121.         try { exeFile.remove(true); } catch(ex) { this.log(ex.message); }
  122.       }
  123.       this._exeFile=this.checkExePlatform(exeFile);
  124.       if(this._exeFile!=null && this.createExecutable()) {
  125.         this.log(this._exeFile.path+" created");
  126.       }
  127.     } catch(ex) {
  128.       this._exeFile=null;
  129.       this.log("Can't init "+exeName+":\n"+ex.message);
  130.     }
  131.     return FlashGotDM.executables[exeName]=this._exeFile;
  132.   }
  133. ,
  134.   checkExePlatform: function(exeFile) {
  135.     return /(\/.*\.exe)|(\\.*\.sh)$/i.test(exeFile.path)?null:exeFile;
  136.   }
  137. ,
  138.   get supported() {
  139.     if(typeof(this._supported) == "boolean") return this._supported;
  140.     if(this.customSupportCheck && this.customSupportCheck()) return true;
  141.     if(!this.exeName) return true;
  142.     if(!this.exeFile) return false;
  143.     
  144.     var dmtest;
  145.     if(typeof(FlashGotDM.dmtests[this.exeName])!="string") {
  146.       const dmtestFile=this.service.tmpDir.clone();
  147.       dmtestFile.append(this.exeName+".test");
  148.       try {
  149.         this.launchSupportTest(dmtestFile);
  150.         this.log(dmtest=this.service.readFile(dmtestFile)); 
  151.       } catch(ex) {
  152.         this.log(ex.message);
  153.         dmtest="";
  154.       }
  155.       FlashGotDM.dmtests[this.exeName]=dmtest;
  156.     } else dmtest=FlashGotDM.dmtests[this.exeName];
  157.     return this._supported = dmtest.indexOf(this.name+"|OK")>-1;
  158.   }
  159. ,
  160.   launchSupportTest: function (testFile) {
  161.     this.runNative(["-o",testFile.path],true);
  162.   },
  163.   
  164.   shouldList: function() {
  165.     return this.supported;
  166.   }
  167. ,
  168.   log: function(msg) {
  169.     this.service.log(msg);
  170.   }
  171. ,
  172.   updateProgress: function(links, idx , len) {
  173.    
  174.     if((idx % 100) == 0) {
  175.       if(!len) {
  176.         links.progress.update(100);
  177.         return;
  178.       }
  179.       links.progress.update(50 + 49 * idx / len);
  180.     }
  181.     
  182.   }
  183. ,
  184.   isValidLink: null
  185. ,
  186.   createJobHeader: function(links, opType) {
  187.     return links.length+";" + this.name + ";" +
  188.       (this.service.getPref(this.codeName + ".quiet." + opType, false)
  189.         ? this.service.OP_QET : opType)
  190.       + ";" + links.folder + ";\n"
  191.   }
  192. ,
  193.   createJobBody: function(links) {
  194.     var job="";
  195.     var l, url;
  196.     const len=links.length;
  197.     this.checkCookieSupport();
  198.     var postData = links.postData || "";
  199.  
  200.     for(var j=0; j<len; j++) {
  201.       job+="\n"+(url=(l=links[j]).href) + "\n" +
  202.            l.description + "\n" +
  203.            this.getCookie(l,links) + "\n"
  204.            + postData;
  205.       this.updateProgress(links,j,len);
  206.     }
  207.     return job;
  208.   }
  209. ,
  210.   createJob: function(links,opType) {
  211.     var job=this.createJobHeader(links,opType) 
  212.     + this.getReferrer(links)
  213.     + this.createJobBody(links)+"\n";
  214.      if(job.substring(job.length-1)!="\n") {
  215.       job+="\n";
  216.      }
  217.     if(typeof(links.document)=="object") {
  218.       job+= links.document.referrer+ "\n" +links.document.cookie;
  219.     } else {
  220.       job+="\n";
  221.     }
  222.     return job;
  223.   }
  224. ,
  225.   _bgJob: true,
  226.   get bgJob() {
  227.     return this._bgJob && this.service.bgProcessing
  228.       ; 
  229.   }
  230. ,
  231.   download: function(links, opType) {
  232.     try {
  233.       links.folder=(links.length>0)?this.selectFolder(links, opType):""; 
  234.       this.performJob(this.createJob(links,opType));
  235.     } catch(ex) {
  236.       this.log(ex.message);
  237.     } finally {
  238.       this.updateProgress(links, 0); // 100%
  239.     }
  240.   }
  241. ,
  242.   getReferrer: function(links) {
  243.     return this.service.getPref("autoReferrer",true) ?
  244.       (links.referrer || 
  245.         typeof(links.document)=="object" && links.document.URL ||
  246.         links[0] && links[0].href || 
  247.         "about:blank"
  248.       ) : this.service.getPref("fakeReferrer","");
  249.   }
  250. ,
  251.   checkCookieSupport: function() {
  252.     this.getCookie=this.cookieSupport && !this.service.getPref("omitCookies")
  253.     ?this._getCookie
  254.     :function() { return ""; }
  255.     ;
  256.   }
  257. ,
  258.   getCookie: function() { return ""; }
  259. ,
  260.   _getCookie: function(link,links) {
  261.     if(!this.cookieSupport) return (this.getCookie=function() { return ""; })();
  262.     
  263.     var host,cookies;
  264.     if(cookies=links.cookies) {
  265.       host=link.host;
  266.       if(host) {
  267.         var c=cookies[host];
  268.         return c?c:"";
  269.       }
  270.       return "";
  271.     }
  272.     
  273.     var j,objCookie;
  274.     const hostCookies={};
  275.     
  276.     var l,parts;
  277.     for(j=links.length; j-->0;) {
  278.       l = links[j];
  279.       parts = l.href.match(/http[s]{0,1}:\/\/([^\/]+\.[^\/]+)/i); // host?
  280.       if(parts) {
  281.         host = parts[1];
  282.         var hpos = host.indexOf("@");
  283.         if(hpos > -1) host = host.substring(hpos + 1);
  284.         hostCookies[l.host = host] = "";
  285.       } else {
  286.         l.host=null;
  287.       }
  288.     }
  289.     
  290.     var cookieHost,cookieTable,tmpCookie;
  291.     const domainCookies={};
  292.  
  293.     for(var iter = this.cookieManager.enumerator; iter.hasMoreElements();) {
  294.       if((objCookie=iter.getNext()) instanceof Components.interfaces.nsICookie) {
  295.         cookieHost=objCookie.host;
  296.         if(cookieHost.charAt(0)==".") {
  297.           cookieHost=cookieHost.substring(1);
  298.           cookieTable=domainCookies;
  299.           if(typeof(tmpCookie=domainCookies[cookieHost])!="string") {
  300.             tmpCookie="";
  301.           }
  302.         } else {
  303.           if(typeof(tmpCookie=hostCookies[cookieHost])!="string") continue;
  304.           cookieTable=hostCookies;
  305.         }
  306.         cookieTable[cookieHost]=tmpCookie.concat(objCookie.name+"="+objCookie.value+"; ");
  307.       }
  308.     }
  309.     
  310.    
  311.     for(cookieHost in hostCookies) {
  312.       var dotPos;
  313.       for(host=cookieHost; (dotPos=host.indexOf('.'))>=0; ) { 
  314.         if(tmpCookie=domainCookies[host]) {
  315.           hostCookies[cookieHost]+=tmpCookie;
  316.         }
  317.         host=host.substring(dotPos+1);
  318.       }
  319.     }
  320.     
  321.     links.cookies=hostCookies;
  322.     return this.getCookie(link, links);
  323.   }
  324. ,
  325.   createJobFile: function(job) {
  326.     const jobFile=this.service.tmpDir.clone();
  327.     jobFile.append("flashgot.fgt");
  328.     jobFile.createUnique(0,0700);
  329.     this.service.writeFile(jobFile, job);
  330.     return jobFile;
  331.   }
  332.   _waitForNative: true,
  333.   get waitForNative() {
  334.     return this._waitForNative && this.service.bgProcessing;
  335.   }
  336. ,
  337.   performJob: function(job) {
  338.     const jobFile=this.createJobFile(job);
  339.     this.runNative([jobFile.path],this.waitForNative);
  340.   }
  341. ,
  342.   createExecutable: function() {
  343.     const exeFile=this.exeFile;
  344.     if(!exeFile) return false;
  345.     
  346.     const cc=Components.classes;
  347.     const ci=Components.interfaces;
  348.     const ios=cc['@mozilla.org/network/io-service;1'].getService(ci.nsIIOService);
  349.     const bis=cc['@mozilla.org/binaryinputstream;1'].createInstance(ci.nsIBinaryInputStream);
  350.     
  351.     var channel;
  352.     bis.setInputStream((
  353.       channel=
  354.         ios.newChannel("chrome://flashgot/content/"+this.exeName,null,null)
  355.     ).open())
  356.     ;
  357.     const bytesCount=channel.contentLength;
  358.     
  359.     const os=cc["@mozilla.org/network/file-output-stream;1"].createInstance(
  360.       ci.nsIFileOutputStream);
  361.     
  362.     try {
  363.       
  364.       os.init(exeFile,0x02 | 0x08, 0700, 0);
  365.       const bos=cc['@mozilla.org/binaryoutputstream;1'].createInstance(ci.nsIBinaryOutputStream);
  366.       bos.setOutputStream(os);
  367.       bos.writeByteArray(bis.readByteArray(bytesCount),bytesCount);
  368.       bos.close();
  369.  
  370.     } catch(ioErr) { // locked?
  371.       try {
  372.         if(exeFile.exists()) { // security check: it must be the right exe!
  373.           const testBis=cc['@mozilla.org/binaryinputstream;1'].createInstance(
  374.             ci.nsIBinaryInputStream);
  375.           testBis.setInputStream(
  376.             (channel=ios.newChannelFromURI(ios.newFileURI(exeFile))).open());
  377.           const error=new Error("Old, corrupt or unlegitemately modified "
  378.             +exeFile.path
  379.             +".\nThe file is locked: please delete it manually\n");
  380.             +ioErr.message;
  381.           if(channel.contentLength!=bytesCount) throw error;
  382.          
  383.           const legitimateData=bis.readByteArray(bytesCount);
  384.           const testData=testBis.readByteArray(bytesCount);
  385.           for(var j=bytesCount; j-->0;) {
  386.             if(legitimateData[j]!=testData[j]) throw new error;
  387.           }
  388.         } else throw ioErr;
  389.       } catch(unrecoverableErr) {
  390.          this.log("Error creating native executable\n"+exeFile.path+"\n"+unrecoverableErr.message);
  391.       }
  392.     } finally {
  393.       os.close();
  394.       bis.close();
  395.     }
  396.     
  397.     return true;
  398.   }
  399. ,
  400.   runNative: function(args,blocking,exeFile) {
  401.     try {
  402.       if(typeof(exeFile)=="object"
  403.         || (exeFile=this.exeFile).exists()
  404.         || this.createExecutable()) {
  405.         const proc=Components.classes['@mozilla.org/process/util;1'].createInstance(
  406.           Components.interfaces.nsIProcess);
  407.         proc.init(exeFile);
  408.         this.log("Running " + exeFile.path + " " + args.join(" ") + " -- " +(blocking ? "blocking" : "async") );
  409.         proc.run(blocking,args,args.length,{});
  410.         if(blocking && proc.exitValue != 0) {
  411.           this.log("Warning: native invocation of\n"
  412.             +exeFile.path
  413.             +"\nwith arguments <"
  414.             +args.join(" ")
  415.             +">\nreturned "+proc.exitValue);
  416.         }
  417.         return proc.exitValue;
  418.       } else {
  419.         this.log("Bad executable "+exeFile);
  420.       }
  421.     } catch(err) {
  422.       this.log("Error running native executable:\n"+exeFile.path+" "+args.join(" ")+"\n"+err.message);
  423.     }  
  424.     return 0xffffffff;
  425.   }
  426. ,
  427.   getWindow: function() {
  428.     return this.service.getWindow();
  429.   }
  430. ,
  431.   selectFolder: function(links, opType) { 
  432.     const cc = Components.classes;
  433.     const ci = Components.interfaces;
  434.    
  435.     const autoPref_FF = "browser.download.useDownloadDir";
  436.     const autoPref_Moz = "browser.download.autoDownload";
  437.     
  438.     var initialDir = null;
  439.     var downloadDir = null;
  440.     links.quickDownload = false;
  441.     
  442.     const pref = cc["@mozilla.org/preferences-service;1"].getService(ci.nsIPrefBranch);
  443.     
  444.     function findDownloadDir(prefName) {
  445.       try {
  446.         downloadDir = initialDir = pref.getComplexValue(prefName, ci.nsILocalFile);
  447.         return prefName;
  448.       } catch(ex) {
  449.         return "";
  450.       }
  451.     }
  452.     const isMulti = opType != this.service.OP_ONE;
  453.     const multiDirPref = "flashgot.multiDir";
  454.     var downloadDirPref = 
  455.                     (isMulti && findDownloadDir(multiDirPref)) ||
  456.                     findDownloadDir("browser.download.dir") ||
  457.                     findDownloadDir("browser.download.downloadDir") || 
  458.                     findDownloadDir("browser.download.defaultFolder") ||
  459.                     "browser.download.dir"; 
  460.     
  461.     if(isMulti) downloadDirPref = multiDirPref;
  462.     
  463.     try {
  464.       links.quickDownload = pref.getBoolPref(autoPref_FF);
  465.     } catch(noFFEx) {
  466.       try {
  467.         links.quickDownload = pref.getBoolPref(autoPref_Moz);
  468.       } catch(noMozEx) {}
  469.     }
  470.    
  471.     if(!this.askPath[opType]) return "";
  472.     
  473.     if(((!isMulti) || this.service.getPref("multiQuiet", false)) && 
  474.         downloadDir && downloadDir.exists() && downloadDir.isDirectory()  && 
  475.         links.quickDownload) {
  476.       return downloadDir.path;
  477.     }
  478.     
  479.     var title;
  480.     try {
  481.       var bundle = cc["@mozilla.org/intl/stringbundle;1"].getService(ci.nsIStringBundleService);
  482.       bundle = bundle.createBundle("chrome://mozapps/locale/downloads/unknownContentType.properties");
  483.       title = bundle.GetStringFromName("myDownloads");
  484.     } catch(ex) {
  485.       title="Download directory";
  486.     }
  487.     title = "FlashGot (" + this.name + ") - " + title;
  488.     
  489.     const fp = cc["@mozilla.org/filepicker;1"].createInstance(ci.nsIFilePicker);
  490.     const win=this.getWindow();
  491.     fp.init(win, title, ci.nsIFilePicker.modeGetFolder);
  492.     try {
  493.       if (initialDir &&  initialDir.exists() && initialDir.isDirectory()) {
  494.         fp.displayDirectory = initialDir;
  495.       }
  496.     } catch (ex) { this.log(ex); }
  497.     
  498.     fp.appendFilters(ci.nsIFilePicker.filterAll);
  499.  
  500.     if (fp.show()==ci.nsIFilePicker.returnOK) {
  501.       var localFile = fp.file.QueryInterface(ci.nsILocalFile);
  502.       pref.setComplexValue(downloadDirPref, ci.nsILocalFile, localFile);
  503.       var path=new String(localFile.path);
  504.       path._fgSelected=true;
  505.       return path;
  506.     }
  507.     
  508.     throw new Error("Download cancelled by user");
  509.   },
  510.   sanitizeWinArg: function(a) {
  511.     return a.replace(/([\|\(\) &\^])/g, "^$1"); 
  512.   },
  513.   supportURLList: function(links, argsTemplate) {
  514.     if(/\[[^\]]*UFILE[^\]]*\]/.test(argsTemplate) && links.length) {
  515.       // we must create a file list
  516.       var sep = this.service.isWindows ? "\r\n" : "\n";
  517.       var urlList = "";
  518.       for(j = 0; j < links.length; j++) {
  519.         urlList += links[j].href + sep;
  520.       }
  521.       links.length = 1;
  522.       return this.createJobFile(urlList).path
  523.     }
  524.     return null;
  525.   }
  526.   
  527. }
  528.  
  529.  
  530.  
  531.  
  532. // *** Unix-like DMS ***********************************************************
  533. function FlashGotDMX(name,cmd,argsTemplate) {
  534.   if(arguments.length!=0) {
  535.     this._init(name);
  536.     const cmds = FlashGotDMX.prototype.unixCmds;
  537.     cmds[cmds.length] = {longName: name, shortName: cmd};
  538.     this.unixCmd = cmd;
  539.     if(argsTemplate) this.argsTemplate = argsTemplate;
  540.   }
  541. }
  542. FlashGotDMX.prototype=new FlashGotDM();
  543. FlashGotDMX.constructor=FlashGotDMX;
  544. FlashGotDMX.prototype.exeName="flashgot.sh";
  545. FlashGotDMX.prototype.cookieSupport=false;
  546. FlashGotDMX.prototype.askPath=[true,true,true];
  547. FlashGotDMX.prototype.unixCmds=[];
  548. FlashGotDMX.prototype.unixShell=null;
  549. FlashGotDMX.prototype.argsTemplate="[URL]";
  550. FlashGotDMX.prototype.launchSupportTest=function(testFile) {
  551.   const cmds = this.unixCmds;
  552.   var script="(\n";
  553.   var cmd;
  554.   for(var j=cmds.length; j-->0;) {
  555.     cmd=cmds[j];
  556.     script+=" [ -x \"`which '"+cmd.shortName+"'`\" ] && echo '"
  557.       +cmd.longName+"|OK' || echo '"+cmd.longName+"|KO'\n"; 
  558.   }
  559.   script+=") > '"+ testFile.path + "'\n"; 
  560.   this.performJob(script,true);
  561. };
  562.  
  563. FlashGotDMX.prototype.createCmdLine = function(parms) {
  564.   return this.unixCmd + " " +
  565.     this.argsTemplate.replace(/\[(.*?)(URL|REFERER|COOKIE|FOLDER|POST|UFILE)(.*?)\]/g,
  566.       function(all, before, parm, after) {
  567.           v = parms[parm]; 
  568.           return typeof(v) != "undefined" && v != null
  569.             ? before + v + after
  570.             : "";
  571.       }
  572.    ) +" &\n";
  573. };
  574. FlashGotDMX.prototype.shellEsc = function(s) {
  575.   return s?s.replace(/([\\\*\?\[\]\$&<>\|\(\)\{\};"'`])/g,"\\$1").replace(/\s/g,"\\ "):null;
  576. };
  577. FlashGotDMX.prototype.createJob=function(links,opType) {
  578.   const shellEsc = this.shellEsc;
  579.   // basic implementation
  580.  
  581.   const folder=shellEsc(links.folder);
  582.   const referrer=shellEsc(this.getReferrer(links));
  583.   const postData=shellEsc(links.postData);
  584.   var job="";
  585.   var l, url;
  586.   this.checkCookieSupport();
  587.   var urlListFile = this.supportURLList(links, this.argsTemplate);
  588.   for(var j = 0, len = links.length; j < len; j++) {
  589.     l = links[j];
  590.     url = l.href;
  591.     job += this.createCmdLine({
  592.       URL: shellEsc(url), 
  593.       REFERER: referrer, 
  594.       COOKIE: shellEsc(this.getCookie(l, links)), 
  595.       FOLDER: folder, 
  596.       POST: postData,
  597.       UFILE: shellEsc(urlListFile)
  598.     });
  599.     this.updateProgress(links, j, len);
  600.   }
  601.   return job;
  602. };
  603. FlashGotDMX.prototype.performJob=function(job,blocking) {
  604.   const jobFile=this.createJobFile("#!"+this.unixShell.path+"\n"+job);
  605.   jobFile.permissions=0700;
  606.   this.runNative([],
  607.     this.waitForNative || (typeof(blocking)!="undefined" && blocking),
  608.     jobFile);
  609. };
  610. FlashGotDMX.prototype.checkExePlatform=function(exeFile) {
  611.   const f=Components.classes["@mozilla.org/file/local;1"].createInstance(
  612.     Components.interfaces.nsILocalFile);
  613.   try {
  614.     f.initWithPath("/bin/sh");
  615.     if(f.exists()) {
  616.       FlashGotDMX.prototype.unixShell=f;
  617.       return exeFile;
  618.     }
  619.     this.log(f.path+" not found");
  620.   } catch(ex) {
  621.     this.log(ex.message);
  622.   }
  623.   return null;
  624. };
  625. FlashGotDMX.prototype.createExecutable=function() {
  626.   return false;
  627. };
  628.  
  629.  
  630.  
  631. // *** Mac OS X DMS ************************************************************
  632. function FlashGotDMMac(name, creatorId, macAppName) {
  633.   if(arguments.length!=0) {
  634.     this._initMac(name, creatorId, macAppName);
  635.   }
  636. }
  637. FlashGotDMMac.prototype=new FlashGotDM();
  638. FlashGotDMMac.constructor=FlashGotDMMac;
  639. FlashGotDMMac.prototype.exeName="flashgot-mac.sh";
  640. FlashGotDMMac.prototype.cookieSupport=false;
  641. FlashGotDMMac.prototype.OSASCRIPT="/usr/bin/osascript";
  642. FlashGotDMMac.prototype.macCreators=[];
  643. FlashGotDMMac.prototype._initMac=function(name, creatorId, macAppName) {
  644.   this._init(name);
  645.   if(creatorId) {
  646.     const creators=FlashGotDMMac.prototype.macCreators;
  647.     creators[creators.length] = {name: name, id: creatorId};
  648.   }
  649.   this.macAppName = macAppName?macAppName:name;
  650. };
  651. FlashGotDMMac.prototype.createScriptLauncher=function(scriptPath) {
  652.   return "#!/bin/sh\n"
  653.     +this.OSASCRIPT+" '"+scriptPath+"'";
  654. };
  655. FlashGotDMMac.prototype.checkExePlatform=function(exeFile) {
  656.   const f=Components.classes["@mozilla.org/file/local;1"].createInstance(
  657.     Components.interfaces.nsILocalFile);
  658.   try {
  659.     f.initWithPath(this.OSASCRIPT);
  660.     if(f.exists()) return exeFile;
  661.     this.log(f.path+" not found");
  662.   } catch(ex) {
  663.     this.log(ex.message);
  664.   }
  665.   return null;
  666. };
  667. FlashGotDMMac.prototype.createExecutable=function() {
  668.   const exeFile=this.exeFile;
  669.   if(exeFile) {
  670.     try {
  671.      const script=this.service.tmpDir.clone();
  672.      script.append("flashgot-test.scpt");
  673.      FlashGotDMMac.prototype.testAppleScript=script;
  674.      script.createUnique(0,0700);
  675.      if(exeFile.exists()) exeFile.remove(true);
  676.      exeFile.create(0,0700);
  677.      this.service.writeFile(exeFile, this.createScriptLauncher(script.path));
  678.      exeFile.permissions=0700;
  679.      return true;
  680.     } catch(ex) {
  681.       this.log(ex.message);
  682.     }
  683.   }
  684.   return false;
  685. };
  686. FlashGotDMMac.prototype.launchSupportTest=function(testFile) {
  687.   const creators=FlashGotDMMac.prototype.macCreators;
  688.   const RESP="    do shell script \"echo >>'"+testFile.path+"' '\" & theName & \"|";
  689.   function response(msg) {
  690.     return RESP+msg+"'\"\n";
  691.   }
  692.   var s="on test(theName, theCreator)\n"
  693.        +" tell app \"Finder\"\n"
  694.        +"  set theResponse to \"KO\"\n"
  695.        +"  try\n"
  696.        +"   get name of application file id theCreator\n"
  697.        +"   if result contains theName then\n"
  698.        +"     set theResponse to \"OK\"\n"
  699.        +"   end if\n"
  700.        +"  on error\n"
  701.        +"  end try\n"
  702.        +"  do shell script \"echo >>'"+testFile.path+"' '\" & theName & \"|\" & theResponse & \"'\"\n"
  703.        +" end tell\n"
  704.        +"end test\n"
  705.        +"\n";
  706.   for(var j=creators.length; j-->0;) {
  707.      s+='get test("'+creators[j].name+'","'+creators[j].id+'")\n'; 
  708.    }
  709.    this.service.writeFile(this.testAppleScript,s);
  710.    this.runNative([],true,this.exeFile);
  711. };
  712. FlashGotDMMac.prototype.performJob=function(job) {
  713.   const script=this.createJobFile(job);
  714.   const launcher=this.createJobFile(this.createScriptLauncher(script.path));
  715.   launcher.permissions=0700;
  716.   this.runNative([],this.waitForNative,launcher);
  717. };
  718. FlashGotDMMac.prototype.createJob=function(links,opType) {
  719.   const referrer=this.getReferrer(links);
  720.   var job = "tell application \""+ this.macAppName+ "\"\n";
  721.   for(var j=0,len=links.length; j<len; j++) {
  722.     job+="GetURL \""+links[j].href+"\" from \""+ referrer  +"\"\n";
  723.     this.updateProgress(links, j, len);
  724.   }
  725.   job+="end tell\n";
  726.   return job;
  727. };
  728.  
  729.  
  730.  
  731. // *** Custom DMS **************************************************************
  732. function FlashGotDMCust(name) {
  733.   if(arguments.length==0 || (!name) || (!name.length)) return;
  734.   name = name.replace(/,/g, " ");
  735.   this._init(name);
  736.   this.prefsBase = "custom." + this.codeName + ".";
  737. }
  738.  
  739. FlashGotDMCust.init = function(service) {
  740.   const names = service.getPref("custom", "").split(/\s*,\s*/);
  741.   for(var j=names.length; j-->0;) {
  742.     new FlashGotDMCust(names[j]);
  743.   }
  744. }
  745.  
  746. FlashGotDMCust.persist = function(service) {
  747.   const dms = FlashGotDM.dms;
  748.   const cdms = [];
  749.   for(var j = dms.length; j-->0;) {
  750.     if(dms[j].custom) cdms.push(dms[j].name);
  751.   }
  752.   service.setPref("custom", cdms.join(","));
  753. }
  754.  
  755. FlashGotDMCust.prototype = new FlashGotDM();
  756. delete FlashGotDMCust.prototype.launchSupportTest;
  757. delete FlashGotDMCust.prototype.exeFile;
  758.  
  759. FlashGotDMCust.constructor = FlashGotDMCust;
  760.  
  761. FlashGotDMCust.prototype.custom = true;
  762. FlashGotDMCust.prototype._supported = true;
  763. FlashGotDMCust.prototype.__defineGetter__("exeFile",function() {
  764.   try {
  765.     return this.service.prefs.getComplexValue(this.prefsBase+"exe", 
  766.       Components.interfaces.nsILocalFile);
  767.   } catch(ex) {
  768.     return null;
  769.   }
  770. });
  771. FlashGotDMCust.prototype.__defineSetter__("exeFile",function(v) {
  772.   try {
  773.     if(v) {
  774.       this.service.prefs.setComplexValue(this.prefsBase+"exe", 
  775.           Components.interfaces.nsILocalFile,v);
  776.       return v;
  777.     }
  778.   } catch(ex) {
  779.     return null;
  780.   }
  781. });
  782.  
  783. FlashGotDMCust.prototype.__defineGetter__("argsTemplate",function() {
  784.   if(this.forcedTemplate) return this.forcedTemplate;
  785.   var t = this.service.getPref(this.prefsBase+"args","[URL]");
  786.   return /['"`]/.test(t) ? this.argsTemplate = t : t;
  787. });
  788. FlashGotDMCust.prototype.__defineSetter__("argsTemplate",function(v) {
  789.   if(!v) {
  790.     v="";
  791.   } else {
  792.     v=v.replace(/['"`]/g,"");
  793.   }
  794.   this.service.setPref(this.prefsBase+"args",v);
  795.   return v;
  796. });
  797.  
  798. FlashGotDMCust.prototype.cookieSupport=false;
  799. FlashGotDMCust.prototype.askPath=[true,true,true];
  800.  
  801. FlashGotDMCust.prototype.download = function(links, opType) {
  802.   const t = this.argsTemplate;
  803.   this.cookieSupport=/\[.*?COOKIE.*?\]/.test(t);
  804.   this.askPath[opType]=/\[.*?FOLDER.*?\]/.test(t);
  805.   var exeFile=this.exeFile;
  806.   // portable hacks
  807.   if(exeFile && !exeFile.exists()) {
  808.     // try changing the first part of path
  809.     var path = exeFile.path;
  810.     var profPath = this.service.profDir.path;
  811.     var pos1, pos2;g
  812.     if(path[1] == ":" && profPath[1] == ":") { 
  813.       // easy, it's Windows, swap drive letter
  814.       path = profPath[0] + path.substring(1);
  815.     } else if(path.indexOf("/mount/") == 0 && profPath.indexOf("/mount/") == 0) {
  816.       pos1 = path.indexOf("/", 7);
  817.       pos2 = profPath.indexOf("/", 7);
  818.       path = "/mount/" + profPath.substring(7, pos2) + path.substring(pos1); 
  819.     } else if((pos1 = path.indexOf("/",1)) > 0 && (pos2 = profPath.indexOf("/", 1)) > 0) {
  820.       path = profPath.substring(0, pos2) + path.substring(pos1);
  821.     } else exeFile = null;
  822.     if(exeFile) {
  823.       exeFile = exeFile.clone().QueryInterface(Components.interfaces.nsILocalFile).initWithPath(path);
  824.       if(!exeFile.exists()) exeFile = null;
  825.     }
  826.   }
  827.   links.exeFile= (exeFile || 
  828.     (exeFile=this.exeFile=this.locateExeFile())) ? exeFile : null;
  829.   FlashGotDM.prototype.download.call(this, links, opType);
  830. };
  831.  
  832. FlashGotDMCust.prototype.locateExeFile = function(name) {
  833.   const cc=Components.classes;
  834.   const ci=Components.interfaces;
  835.   if(!name) name=this.name;
  836.   var title=this.service.getString("custom.exeFile");
  837.   title='FlashGot ('+name+') - '+title;
  838.   
  839.   const fp = cc["@mozilla.org/filepicker;1"].createInstance(ci.nsIFilePicker);
  840.   const win=this.getWindow();
  841.   fp.init(win, title, ci.nsIFilePicker.modeOpen);
  842.   fp.appendFilters(ci.nsIFilePicker.filterApps);
  843.   fp.appendFilters(ci.nsIFilePicker.filterAll);
  844.  
  845.   if (fp.show() == ci.nsIFilePicker.returnOK) {
  846.     var file = fp.file.QueryInterface(ci.nsILocalFile);
  847.     if(file.exists()) {
  848.       return file;
  849.     }
  850.   }
  851.   return null;
  852. };
  853. FlashGotDMCust.prototype.PLACEHOLDERS = ["URL", "REFERER", "COOKIE", "FOLDER", "POST", "UFILE", "CFILE"];
  854. FlashGotDMCust.prototype.postSupport = true;
  855. FlashGotDMCust.prototype._addParts=function(a, s) {
  856.   var parts=s.split(/\s+/);
  857.   var k, p;
  858.   for(k in parts) {
  859.     if((p = parts[k])) {
  860.       a[a.length] = p;
  861.     }
  862.   }
  863. };
  864. FlashGotDMCust.prototype.makeArgs = function(parms) {
  865.  
  866.   const args = [];
  867.   var t = this.argsTemplate;
  868.   var j, v, len, s;
  869.   
  870.   var idx;
  871.   
  872.   for(var m; 
  873.       m = t.match( /\[([\s\S]*?)(\S*)\b(URL|REFERER|COOKIE|FOLDER|POST|CFILE|UFILE)\b(\S*?)([\s\S]*?)\]/); 
  874.       t = t.substring(idx + m[0].length) 
  875.      ) {
  876.  
  877.     if((idx = m.index) > 0) {
  878.       this._addParts(args, t.substring(0, idx));
  879.     }
  880.     
  881.     v = parms[m[3]];
  882.     if(!v) continue;
  883.     
  884.     this._addParts(args, m[1]);
  885.     args[args.length] = m[2] + v + m[4];
  886.     this._addParts(args, m[5]);
  887.   }
  888.   
  889.   if(t.length) {
  890.     this._addParts(args, t);
  891.   }
  892.   return args;
  893. };
  894.  
  895. FlashGotDMCust.prototype.createJob=function(links, opType) {
  896.   return { links: links, opType: opType };
  897. };
  898.  
  899. FlashGotDMCust.prototype.performJob=function(job) {
  900.   const links = job.links;
  901.   const exeFile = links.exeFile;
  902.   if(links.length < 1 || !exeFile) return;
  903.   
  904.   const folder=links.folder;
  905.   const referrer = this.getReferrer(links);
  906.   const postData = links.postData;
  907.   
  908.   this.checkCookieSupport();
  909.   var cookieFile;
  910.   if(this.service.getPref("omitCookies")) {
  911.     cookieFile = null;
  912.   } else {
  913.     cookieFile = this.service.profDir.clone();
  914.     cookieFile.append("cookies.txt");
  915.     cookieFile = cookieFile.path;
  916.   }
  917.  
  918.   var urlListFile = this.supportURLList(links, this.argsTemplate);
  919.   var l;
  920.   for(var j = 0, len = links.length; j < len; j++) {
  921.     l = links[j];
  922.     this.runNative(
  923.       this.makeArgs({
  924.         URL: l.href, 
  925.         REFERER: referrer, 
  926.         COOKIE: this.getCookie(l, links), 
  927.         FOLDER: folder, 
  928.         POST: postData,
  929.         CFILE: cookieFile,
  930.         UFILE: urlListFile
  931.        }),
  932.        false, exeFile);
  933.     this.updateProgress(links, j, len);
  934.   } 
  935. };
  936. FlashGotDMCust.prototype.checkExePlatform=function(exeFile) {
  937.   return exeFile;
  938. };
  939. FlashGotDMCust.prototype.createExecutable=function() {
  940.   return false;
  941. };
  942.  
  943.  
  944. // *****************************************************************************
  945. // END DMS CLASSES
  946. // *****************************************************************************
  947.  
  948. // DMS initialization
  949.  
  950. FlashGotDM.initDMS=function(service) {
  951.   var dm;
  952.   
  953.   new FlashGotDM("BitComet");
  954.  
  955.   new FlashGotDM("Download Accelerator Plus");
  956.   
  957.   new FlashGotDM("Download Master");
  958.   
  959.   new FlashGotDM("FlashGet");
  960.   
  961.   dm=new FlashGotDM("Free Download Manager");
  962.   dm._waitForNative=false;
  963.   
  964.   new FlashGotDM("FreshDownload");
  965.   
  966.   dm=new FlashGotDM("GetRight");
  967.   dm.supportsMetalink = true;
  968.   dm.super_download=FlashGotDM.prototype.download;
  969.   dm.super_createJob=FlashGotDM.prototype.createJob;
  970.   dm.download=function(links, opType) {
  971.     const service=this.service;
  972.     if(opType == service.OP_ONE && !service.getPref("GetRight.quick")) {
  973.       opType = service.OP_SEL;
  974.     }
  975.     this.super_download(links, opType);
  976.   };
  977.   dm.createJob=function(links, opType) {
  978.     const service=this.service;
  979.     var folder=links.folder;
  980.     if(!(folder && folder._fgSelected)) folder=false;
  981.     
  982.     var referrer=this.getReferrer(links);
  983.     
  984.     switch(opType) {
  985.       case service.OP_ONE:
  986.         var job = this.super_createJob(links, opType).replace(/; /g, ";");
  987.         if(this.service.getPref("GetRight.old")) job+="\nold";
  988.         return job;
  989.       case service.OP_SEL:
  990.       case service.OP_ALL:
  991.         var urlList = "";
  992.         var referrerLine=(referrer && referrer.length>0)?"\r\nReferer: "+referrer+"\r\n":"\r\n";
  993.         var l, k, len, decodedURL, urlParts, fileSpec, cookie;
  994.         for(var j = 0; j < links.length; j++) {
  995.           l=links[j];
  996.           
  997.           if(folder) {
  998.             decodedURL=l.href;
  999.             try { decodedURL=decodeURI(decodedURL) } catch(ex) {};
  1000.             urlParts=decodedURL.match(/\/\/.+[=\/]([^\/]+\.\w+)/);
  1001.             if(!urlParts) urlParts=l.href.match(/.*\/(.*\w+.*)/);
  1002.             if(urlParts && (fileSpec=urlParts[1])
  1003.               // && (links.length==1 ||  !/\.(php|[\w]?htm[l]?|asp|jsp|do|xml|rdf|\d+)$/i.test(fileSpec))
  1004.              ) {  
  1005.               urlParts=fileSpec.match(/(.*\.\w+).*/);
  1006.               if(urlParts) fileSpec=urlParts[1];
  1007.               fileSpec="File: "+folder+"\\"+fileSpec.replace(/[^\w\.-]/g,'_')+"\r\n";
  1008.             } else continue;
  1009.           } else fileSpec="";
  1010.           
  1011.           urlList+="URL: "+l.href
  1012.             +"\r\nDesc: "+l.description + "\r\n" + fileSpec;
  1013.           
  1014.             if(l.md5) {
  1015.             urlList += "MD5: " + l.md5 + "\r\n";
  1016.           }
  1017.           if(l.sha1) {
  1018.             urlList += "SHA1: " + l.sha1+ "\r\n";
  1019.           }
  1020.           if(l.metalinks) {
  1021.             for(k = 0, len = Math.min(16, l.metalinks.length); k < len; k++) {
  1022.               urlList += "Alt: " + l.metalinks[k] + "\r\n";
  1023.             }
  1024.           } else {
  1025.             urlList += referrerLine;
  1026.             if(cookie=this.getCookie(l, links)) {
  1027.               urlList+="Cookie: " + cookie + "\r\n";
  1028.             }
  1029.           }
  1030.           this.updateProgress(links, j, len);
  1031.         }
  1032.         var file = service.tmpDir.clone();
  1033.         file.append("flashgot.grx");
  1034.         file.createUnique(0,0600);
  1035.         var charset=null;
  1036.         try {
  1037.           charset=service.getPref("GetRight.charset",
  1038.             service.prefService.QueryInterface(Components.interfaces.nsIPrefBranch
  1039.             ).getComplexValue("intl.charset.default",
  1040.               Components.interfaces.nsIPrefLocalizedString).data);
  1041.         } catch(ex) {}
  1042.         service.writeFile(file, urlList, charset);
  1043.         referrer=file.path;
  1044.         break;
  1045.     }
  1046.     var cmdOpts="/Q";
  1047.     if(service.getPref("GetRight.autostart",false)) { // CHECK ME!!!
  1048.       cmdOpts+="\n /AUTO";
  1049.     }
  1050.     return this.createJobHeader({ length: 0, folder: "" },opType) +
  1051.       referrer + "\n" + cmdOpts;
  1052.   };
  1053.   dm.askPath=[false,true,true];
  1054.   
  1055.   new FlashGotDM("GigaGet");
  1056.   
  1057.   new FlashGotDM("HiDownload");
  1058.   new FlashGotDM("InstantGet");
  1059.   
  1060.   dm = new FlashGotDM("iGetter Win");
  1061.   dm.__defineGetter__("supported", 
  1062.     function() { return  "nsIGetterMoz" in Components.interfaces; });
  1063.   dm.createJob = function(links, opType) {
  1064.     var job = this.getReferrer(links) + "\r\n";
  1065.     for(var j=0; j < links.length; j++) {
  1066.       job += links[j].href + "\r\n" + links[j].description + "\r\n";
  1067.     }
  1068.     return job;
  1069.   };
  1070.   dm.performJob = function(job) {
  1071.     const file = this.createJobFile(job);
  1072.     delete job;
  1073.     Components.classes["@presenta/iGetter"]
  1074.               .getService(Components.interfaces.nsIGetterMoz)
  1075.               .NewURL(file.path);
  1076.     if(file.exists()) file.remove(0);
  1077.   };
  1078.   
  1079.   new FlashGotDM("Internet Download Accelerator");
  1080.   (new FlashGotDM("Internet Download Manager")).postSupport = true;
  1081.  
  1082.   var lg2002=new FlashGotDM("LeechGet 2002");
  1083.   var lg2004=new FlashGotDM("LeechGet");
  1084.   lg2004._bgJob=lg2002._bgJob=false;
  1085.   lg2004.super_createJob=lg2002.super_createJob=FlashGotDM.prototype.createJob;
  1086.   lg2004.createJob=lg2002.createJob=function(links, opType) {
  1087.     const service=this.service;
  1088.     var referrer;
  1089.     switch(opType) {
  1090.       case service.OP_ONE:
  1091.         return this.super_createJob(links, links.quickDownload?service.OP_ONE:service.OP_SEL);
  1092.       case service.OP_SEL:
  1093.         var htmlDoc="<html><head><title>FlashGot selection</title></head><body>";
  1094.         var l;
  1095.         for(var j=0, len=links.length; j<len; j++) {
  1096.           l=links[j];
  1097.           var des=l.description;
  1098.           var tag=l.tagName?l.tagName.toLowerCase():"";
  1099.           htmlDoc=htmlDoc.concat(tag=="img"
  1100.             ?"<img src=\""+l.href+"\" alt=\""+des
  1101.               +"\" width=\""+l.width+"\" height=\""+l.height+
  1102.               "\" />\n"
  1103.             :"<a href=\""+l.href+"\">"+des+"</a>\n");
  1104.           this.updateProgress(links,j,len);
  1105.         }
  1106.         referrer = service.httpServer.addDoc(
  1107.           htmlDoc.concat("</body></html>")
  1108.         );
  1109.         break;
  1110.        default:
  1111.         referrer=links.document.URL;
  1112.         if(referrer.match(/^\s*file:/i)) { // fix for local URLs
  1113.           // we serve local URLs through built-in HTTP server...
  1114.           return this.createJob(links,service.OP_SEL);
  1115.         }
  1116.     }
  1117.     return this.createJobHeader({ length: 0, folder: "" },opType)+referrer+"\n";
  1118.   };
  1119.  
  1120.   new FlashGotDM("Net Transport");
  1121.   new FlashGotDM("Net Transport 2");
  1122.   new FlashGotDM("NetAnts");
  1123.   new FlashGotDM("Mass Downloader");
  1124.   new FlashGotDM("Orbit");
  1125.   
  1126.   (new FlashGotDM("ReGet")).postSupport = true;
  1127.   
  1128.   const httpFtpValidator = function(url) {
  1129.     return /^(http:|ftp:)/.test(url);
  1130.   };
  1131.   dm=new FlashGotDM("Star Downloader");
  1132.   dm.cookieSupport=false;
  1133.   dm.isValidLink=httpFtpValidator;
  1134.   dm._waitForNative=false;
  1135.   
  1136.   dm=new FlashGotDM("TrueDownloader");
  1137.   dm.isValidLink=httpFtpValidator;
  1138.   dm._waitForNative=false;
  1139.   
  1140.   new FlashGotDM("Thunder");
  1141.   new FlashGotDM("Thunder (Old)");
  1142.   
  1143.   dm = new FlashGotDM("WellGet");
  1144.   dm.getRelativeExe = function() {
  1145.     try {
  1146.       return this.service.prefs.getComplexValue("WellGet.path", Components.interfaces.nsILocalFile);
  1147.     } catch(ex) {}
  1148.     return null;
  1149.   };
  1150.   dm.customSupportCheck = function() {
  1151.      try {
  1152.        var wellGetExe = this.getRelativeExe();
  1153.        var currentPath = wellGetExe.path;
  1154.        wellGetExe.initWithPath(this.service.profDir.path.substring(0,2) + dir.path.substring(2));
  1155.        if(wellGetExe.path != currentPath) {
  1156.           this.service.prefs.setComplexValue("WellGet.path", wellGetExe);
  1157.        }
  1158.        return wellGetExe.exists() && wellGetExe.isExecutable();
  1159.      } catch(ex) {
  1160.      }
  1161.      return false;
  1162.   };
  1163.   dm.createJob = function(links, opType) {
  1164.     var job = FlashGotDM.prototype.createJob.call(this, links, opType);
  1165.     var wellGetExe = this.getRelativeExe();
  1166.     if(wellGetExe) job += "\n" + wellGetExe.path;
  1167.     return job;
  1168.   };
  1169.   dm.shouldList = function() { return true; }
  1170.  
  1171.   dm=new FlashGotDMX("Aria", "aria", "[-r REFERER] [-d FOLDER] -g [URL]");
  1172.   dm.createJob=function(links,opType) {
  1173.     return FlashGotDMX.prototype.createJob.call(this,links,opType) + "\nsleep 4\n" + this.unixCmd+" -s &\n";
  1174.   };
  1175.   dm._waitForNative=false;
  1176.   
  1177.   dm=new FlashGotDMX("Downloader 4 X (nt)","nt");
  1178.   dm.createJob=function(links,opType) {
  1179.     return this.unixCmd+"&\nsleep 1\n" +
  1180.       (links.folder && links.folder._fgSelected
  1181.       ? this.unixCmd + " -d '"+links.folder+"'\n"
  1182.       :"") + 
  1183.       FlashGotDMX.prototype.createJob.call(this,links,opType);
  1184.   };
  1185.   
  1186.   dm=new FlashGotDMX("Downloader 4 X","d4x","[--referer REFERER] [--directory FOLDER] [-a URL] [--al POST] [COOKIE]");
  1187.   dm.askPath=[false, true, true];
  1188.   dm.postSupport = true;
  1189.   dm.createJob = function(links, opType) {
  1190.     const service = this.service;
  1191.     const shellEsc = this.shellEsc;
  1192.     const referrer = shellEsc(this.getReferrer(links));
  1193.     const folder = links.folder._fgSelected && links.folder || null;
  1194.     const quiet = service.getPref(this.codeName + ".quiet",false);
  1195.     const len = links.length;
  1196.     var job;
  1197.     
  1198.     if(len>0) {
  1199.       
  1200.        var urls="";
  1201.        for(var j=0; j<len; j++) {
  1202.          urls+=" "+shellEsc(links[j].href);
  1203.          this.updateProgress(links, j, len);
  1204.        }
  1205.  
  1206.        var promptURLs_fakePost = null;
  1207.        var quietURLs_fakeCookie = null;
  1208.        
  1209.        if(quiet) {
  1210.          quietURLs_fakeCookie = urls;
  1211.          urls = null;
  1212.        } else if(len>1) {
  1213.          promptURLs_fakePost = urls;
  1214.          urls = null;
  1215.        }
  1216.        job = this.createCmdLine({
  1217.           URL: urls, 
  1218.           REFERER: referrer,
  1219.           COOKIE: quietURLs_fakeCookie || null,
  1220.           FOLDER: folder,
  1221.           POST: promptURLs_fakePost
  1222.        });
  1223.     } else job = "";
  1224.     
  1225.     return job;
  1226.   };
  1227.   
  1228.   dm=new FlashGotDMX("GNOME Gwget","gwget");
  1229.   dm.askPath=ASK_NEVER;
  1230.   dm.createJob=function(links, opType) {
  1231.     if(opType == service.OP_ALL) {
  1232.       links.length = 1;
  1233.       links[0].href = links.document.URL;
  1234.       opType = service.OP_ONE;
  1235.     }
  1236.     return FlashGotDMX.prototype.createJob.call(this, links, opType)
  1237.   } 
  1238.   
  1239.   dm=new FlashGotDMX("KDE KGet","kget");
  1240.   dm.askPath=ASK_NEVER;
  1241.   
  1242.   if(service.isWindows) {
  1243.     new FlashGotDM("wxDownload Fast");
  1244.   } else {
  1245.     dm=new FlashGotDMX("wxDownload Fast","wxdfast", "[-reference REFERER] [-destination FOLDER] [-list UFILE]");
  1246.     dm.askPath = ASK_NEVER;
  1247.   }
  1248.  
  1249.   dm =new FlashGotDMX("cURL","curl", '-L -O [--referer REFERER] [-b COOKIE] [-d POST] [URL]');
  1250.   dm.postSupport = true;
  1251.   dm.createJob=function(links,opType) {
  1252.     var job="[ -x \"`which 'xterm'`\" ] &&  CURL_CMD='xterm -e curl' || CURL_CMD='curl'\n";
  1253.     if (links.folder) job += "cd '"+links.folder+"'\n";
  1254.     this.unixCmd = "$CURL_CMD";
  1255.     return job + FlashGotDMX.prototype.createJob.call(this,links,opType);
  1256.   };
  1257.  
  1258.  
  1259.   function FlashGotDMSD(version) {
  1260.     this._initMac(version > 3 ? "Speed Download" : ("Speed Download " + version), "Spee");
  1261.     this.version = version;
  1262.     if(version > 2) {
  1263.       this.cookieSupport = true;
  1264.       this.postSupport = true;
  1265.     }
  1266.   };
  1267.   
  1268.   FlashGotDMSD.prototype=new FlashGotDMMac();
  1269.   FlashGotDMSD.prototype.createJob=function(links,opType) {
  1270.     var job = "tell app \""+ this.macAppName+ "\" to AddURL {";
  1271.     var urlList=[];
  1272.     var cookieList=[];
  1273.     var l;
  1274.     for(var j=0,len=links.length; j<len; j++) {
  1275.       l=links[j];
  1276.       urlList[urlList.length] = '"'+l.href+'"';
  1277.       if(this.cookieSupport) {
  1278.         cookieList[cookieList.length] = '"'+this.getCookie(l,links)+'"';
  1279.       }
  1280.       this.updateProgress(links, j, len);
  1281.     }
  1282.     job+=urlList.join(',')+"}";
  1283.     if(this.postSupport) {
  1284.       if(links.postData) { 
  1285.         job+=' with form data "'+links.postData+'"';
  1286.       }
  1287.       const referer=this.getReferrer(links);
  1288.       if(referer && referer.length) {
  1289.         job+=' from "'+referer+'"';
  1290.       }
  1291.       if(cookieList.length) {
  1292.         job+=' with cookies {' + cookieList.join(',') + '}';
  1293.       }
  1294.     }  
  1295.     return job;
  1296.   };
  1297.   
  1298.   new FlashGotDMSD(2);
  1299.   new FlashGotDMSD(3);
  1300.   new FlashGotDMSD(3.5);
  1301.   
  1302.   new FlashGotDMMac("iGetter","iGET");
  1303.   
  1304.   FlashGotDMCust.init(service);
  1305.   service.sortDMS();
  1306.   
  1307. };
  1308.  
  1309. // *****************************************************************************
  1310. // HTTP interceptor (nsIURIContentListener + http-on-modify-request observer)
  1311. // *****************************************************************************
  1312.  
  1313. function HttpInterceptor(service) {
  1314.   this.service = service;
  1315.  
  1316.   Components.classes["@mozilla.org/uriloader;1"].getService(
  1317.     Components.interfaces.nsIURILoader).registerContentListener(this);
  1318. }
  1319.  
  1320. HttpInterceptor.prototype = {
  1321.   service: null,
  1322.   
  1323.   autoStart: false,
  1324.   interceptAll: true,
  1325.   bypassAutoStart: false,
  1326.   forceAutoStart: false,
  1327.   
  1328.   lastPost: null, // last uploadChannel
  1329.  
  1330.   interfaces: [
  1331.     Components.interfaces.nsIURIContentListener,
  1332.     Components.interfaces.nsIObserver, 
  1333.     Components.interfaces.nsIWeakReference,
  1334.     Components.interfaces.nsISupportsWeakReference,
  1335.     Components.interfaces.nsISupports
  1336.   ],
  1337.   
  1338.   QueryInterface: function(iid) {
  1339.      xpcom_checkInterfaces(iid, this.interfaces, Components.results.NS_ERROR_NO_INTERFACE);
  1340.      return this;
  1341.   },
  1342.   
  1343.   
  1344.   // fake weak reference support, pretty useless but necessary to avoid a 1.9+ assertion
  1345.   QueryReferent: function(iid) {
  1346.     return this;
  1347.   },
  1348.   GetWeakReference: function() {
  1349.     return this;
  1350.   },
  1351.   
  1352.   setup: function() { // profile initialization
  1353.     this.autoStart = this.service.getPref("autoStart", false);
  1354.     this.interceptAll = this.service.getPref("interceptAll", true);
  1355.   },
  1356.   
  1357.   dispose: function() {
  1358.     Components.classes["@mozilla.org/uriloader;1"].getService(
  1359.         Components.interfaces.nsIURILoader).unRegisterContentListener(this);
  1360.   },
  1361.   
  1362.   log: function(msg) {
  1363.     this.service.log(msg);
  1364.   },
  1365.   
  1366.   _shouldIntercept: function(contentType) {
  1367.     dump("FG: _shouldIntercept("+contentType+")\n");
  1368.     if(this.bypassAutoStart) return false;
  1369.     const service = this.service;
  1370.     if(!(service.DMS && service.DMS.found)) return false;
  1371.     if(this.forceAutoStart) return true;
  1372.     
  1373.     if(!this.autoStart) return false;
  1374.     
  1375.     if(this.interceptAll &&
  1376.       !/\bxpinstall|text|xml|vnd\.mozilla\b/.test(contentType)) {
  1377.       return true;
  1378.     }
  1379.  
  1380.     if(contentType == "application/x-unknown-content-type") return false;
  1381.     var ms = Components.classes['@mozilla.org/uriloader/external-helper-app-service;1']
  1382.                      .getService(Components.interfaces.nsIMIMEService);
  1383.     const exts = service.extensions;
  1384.     for(var j = exts.length; j-- > 0;) {
  1385.       try{
  1386.         if(contentType == ms.getTypeFromExtension(exts[j])) return true;
  1387.       } catch(e) {}
  1388.     }
  1389.   }
  1390.   _willHandle: function(url, contentType) {
  1391.     if(!/^(http|https|ftp|sftp|rtsp|mms):/i.test(url) ) {
  1392.       if((/^\s*javascript/i).test(url)) this.log("JavaScript url intercepted: "+url);
  1393.       return false;
  1394.     }
  1395.     return true;
  1396.   }
  1397. ,
  1398.   extractPostData: function(channel) {
  1399.     if(channel instanceof Components.interfaces.nsIUploadChannel &&
  1400.        channel.uploadStream instanceof Components.interfaces.nsISeekableStream) {
  1401.       this.log("Extracting post data...");
  1402.       try {
  1403.         channel.uploadStream.seek(0,0);
  1404.         const sis=Components.classes[
  1405.           '@mozilla.org/scriptableinputstream;1'].createInstance(
  1406.           Components.interfaces.nsIScriptableInputStream);
  1407.         sis.init(channel.uploadStream);
  1408.         var postData=sis.read(sis.available()).replace(/\s$/,'').split(/[\r\n]/);
  1409.         return postData[postData.length-1];
  1410.       } catch(ex) {
  1411.         this.log(ex.message);
  1412.       } finally {
  1413.          sis.close();
  1414.       }
  1415.     }
  1416.     return null;
  1417.   },
  1418.   /* nsIURIContentListener */
  1419.   
  1420.   canHandleContent: function(contentType, isContentPreferred, desiredContentType) {
  1421.     dump("FG: canHandleContent "+contentType+")\n");
  1422.     return this._shouldIntercept(contentType);
  1423.   }
  1424. ,
  1425.   lastRequest: null,
  1426.   doContent: function(contentType, isContentPreferred, channel, contentHandler) {
  1427.     const ci = Components.interfaces;
  1428.     channel.QueryInterface(ci.nsIChannel);
  1429.     const uri = channel.URI;
  1430.     dump("FG: doContent " +contentType + " " + uri.spec + "\n");
  1431.     if(!this._willHandle(uri.spec, contentType)) {
  1432.       throw new Error("FlashGot not interested in " + contentType + " from " + uri.spec);
  1433.     }
  1434.     
  1435.     this.log("Intercepting download...");
  1436.  
  1437.     const pathParts=uri.path.split(/\//);
  1438.     var links = [ {
  1439.      href: channel.URI.spec, 
  1440.      description: pathParts[pathParts.length-1],
  1441.     } ];
  1442.     
  1443.     
  1444.     
  1445.     
  1446.     if(channel instanceof ci.nsIHttpChannel) {
  1447.       links.referrer = channel.referrer && channel.referrer.spec || "";
  1448.       links.postData = this.extractPostData(channel);
  1449.     } 
  1450.     var firstAttempt;
  1451.     if(contentHandler) {
  1452.       this.lastRequest = null;
  1453.       firstAttempt = true;
  1454.       this.forceAutoStart = false;
  1455.     } else {
  1456.       var requestLines = [ channel.requestMethod, links[0].href, links.referrer || "", links.postData || ""].join("\n\n");
  1457.       firstAttempt = this.lastRequest != requestLines;
  1458.       this.lastRequest = requestLines;
  1459.     }
  1460.     
  1461.     if(firstAttempt) {
  1462.       if(this.service.download(links)) {
  1463.         this.log("...interception done!");
  1464.       } else {
  1465.          throw new Error("Can't download from this URL: "+uri.spec);
  1466.       }
  1467.     } else {
  1468.       dump("Second attempt, skipping.");
  1469.       this.lastRequest = null;
  1470.       this.forceAutoStart = false;
  1471.     }
  1472.     
  1473.     if(!channel.isPending()) { 
  1474.       try {
  1475.         channel.requestMethod = "HEAD";
  1476.         channel.loadFlags = ci.nsIChannel.LOAD_RETARGETED | ci.nsIChannel.LOAD_RETARGETED_DOCUMENT_URI | ci.nsICachingChannel.LOAD_ONLY_FROM_CACHE;
  1477.       } catch(e) {}
  1478.     }
  1479.     channel.cancel(0x804b0002 /* NS_BINDING_ABORTED */); 
  1480.  
  1481.     this.log("Original request cancelled.");
  1482.     
  1483.     return true;
  1484.   },
  1485.   contentHandler: {
  1486.       onStartRequest: function(request, context) { 
  1487.         throw "cancelled"; 
  1488.       }, 
  1489.       onStopRequest: function() {}, 
  1490.       onDataAvailable: function() {}
  1491.    }
  1492. ,
  1493.   isPreferred: function(contentType, desiredContentType) {
  1494.     dump("FG: isPreferred("+contentType+","+desiredContentType+")\n");
  1495.     return this._shouldIntercept(contentType);
  1496.   }
  1497. ,
  1498.   onStartURIOpen: function(uri) {
  1499.     dump("FG: onStartURIOpen "+ uri + (uri && uri.spec) + "\n");
  1500.     return false;
  1501.   }
  1502. ,
  1503.   /* http-on-modify-request Observer */
  1504.   observe: function(channel, topic, data) {
  1505.     if(channel instanceof Components.interfaces.nsIHttpChannel) {
  1506.       
  1507.       if(this.forceAutoStart) {
  1508.         this.doContent("flashgot/forced", true, channel, null);
  1509.         return;
  1510.       }
  1511.       if(channel instanceof Components.interfaces.nsIUploadChannel) {
  1512.         this.lastPost = channel;
  1513.       }
  1514.     }
  1515.   }
  1516. }
  1517.  
  1518.  
  1519.  
  1520.  
  1521. // *****************************************************************************
  1522. // XPCOM Service
  1523. // *****************************************************************************
  1524.  
  1525. const SHUTDOWN="profile-before-change";
  1526. const STARTUP="profile-after-change";
  1527.  
  1528. function FlashGotService() {
  1529.   
  1530.   const osvr=Components.classes['@mozilla.org/observer-service;1'].getService(
  1531.     Components.interfaces.nsIObserverService);
  1532.   
  1533.   osvr.addObserver(this, SHUTDOWN, false);
  1534.   osvr.addObserver(this, "xpcom-shutdown", false);
  1535.   osvr.addObserver(this, STARTUP, false);
  1536.   
  1537.   this.interceptor = new HttpInterceptor(this);
  1538.   osvr.addObserver(this.interceptor, "http-on-modify-request", true);
  1539.   
  1540.   
  1541. }
  1542.  
  1543. FlashGotService.prototype = {
  1544.   OP_ONE: 0, 
  1545.   OP_SEL: 1,
  1546.   OP_ALL: 2,
  1547.   OP_QET: 3
  1548. ,
  1549.   VERSION: "0.6.4"
  1550. ,
  1551.   get wrappedJSObject() {
  1552.     return this;
  1553.   }
  1554. ,
  1555.   unregister: function() {
  1556.     try {
  1557.       const osvr=Components.classes['@mozilla.org/observer-service;1'].getService(
  1558.       Components.interfaces.nsIObserverService);
  1559.       osvr.removeObserver(this, SHUTDOWN);
  1560.       osvr.removeObserver(this, "xpcom-shutdown");
  1561.       osvr.removeObserver(this, STARTUP);
  1562.       osvr.removeObserver(this.interceptor, "http-on-modify-request");
  1563.       this.interceptor.dispose();
  1564.       this.interceptor = null;
  1565.     } catch(ex) {
  1566.       this.log("Error unregistering service as observer: "+ex);
  1567.     }
  1568.   }
  1569. ,
  1570.   QueryInterface: function(iid) {
  1571.      xpcom_checkInterfaces(iid,SERVICE_IIDS,Components.results.NS_ERROR_NO_INTERFACE);
  1572.      return this;
  1573.   }
  1574. ,
  1575.   /* nsIObserver */  
  1576.   observe: function(subject, topic, data) {
  1577.     if(subject == this.prefs) {
  1578.       this.syncPrefs(data);
  1579.     } else {
  1580.       switch(topic) {
  1581.         case "xpcom-shutdown":
  1582.           this.unregister();
  1583.           break;
  1584.         case SHUTDOWN: 
  1585.           this.cleanup();
  1586.           break;
  1587.         case STARTUP:
  1588.           this.initGlobals();
  1589.           this.interceptor.setup();
  1590.           break;
  1591.       }
  1592.     }
  1593.   }
  1594. ,
  1595.   syncPrefs: function(name) {
  1596.     this.logEnabled=this.getPref("logEnabled",true);
  1597.     if(name) {
  1598.       switch(name) {
  1599.         case "hide-icons":
  1600.           var w;
  1601.           for(var wins=this.windowMediator.getEnumerator(null); wins.hasMoreElements();) {
  1602.              w=wins.getNext();
  1603.              if(typeof(w.gFlashGot)=="object" && w.gFlashGot.toggleMainMenuIcon) {
  1604.                w.gFlashGot.toggleMainMenuIcon();
  1605.              }
  1606.           }
  1607.         break;
  1608.         
  1609.         case "autoStart":
  1610.         case "interceptAll":
  1611.           this.interceptor[name] = this.getPref(name);
  1612.         break;
  1613.       }
  1614.     }
  1615.   }
  1616. ,
  1617.   
  1618.   get defaultDM() {
  1619.     return this.getPref("defaultDM",null);
  1620.   }
  1621. ,
  1622.   set defaultDM(name) {
  1623.     this.setPref("defaultDM", name);
  1624.     return name;
  1625.   }
  1626. ,
  1627.   get tmpDir() {
  1628.     return this.globals.tmpDir; 
  1629.   }
  1630. ,
  1631.   get profDir() {
  1632.     return this.globals.profDir; 
  1633.   }
  1634. ,
  1635.  
  1636.   get isWindows() {
  1637.     return ("nsIWindowsShellService" in Components.interfaces) || ("@mozilla.org/winhooks;1" in Components.classes);
  1638.   }
  1639. ,
  1640.   get DMS() {
  1641.     return this.globals.DMS;
  1642.   }
  1643. ,
  1644.   get extensions() {
  1645.     var s = this.getPref("extensions", "");
  1646.     return s ? s.split(',') : [];
  1647.   }
  1648. ,
  1649.   set extensions(v) {
  1650.     var arr=null;
  1651.     var s = typeof(v)=="string" 
  1652.       ? v : typeof(v)=="object" && typeof(v.join) == "function" 
  1653.         ? (arr=v).join(',').replace(/[^\w\-,]/g,"") : "";
  1654.     this.setPref("extensions", s);
  1655.     return arr ? arr:[];
  1656.   }
  1657. ,
  1658.   addExtension: function(ext) {
  1659.     if(ext) {
  1660.       var extensions = this.extensions;
  1661.       if(!this.extensionExists(ext, extensions)) {
  1662.         extensions[extensions.length] = ext;
  1663.         extensions.sort();
  1664.         this.extensions=extensions;
  1665.         return true;
  1666.       }
  1667.     }
  1668.     return false;
  1669.   }
  1670. ,
  1671.   removeExtension: function(ext) {
  1672.     var extensions=this.extensions;
  1673.     var j=this.indexOfExtension(ext,extensions);
  1674.     if(j>-1) {
  1675.       extensions.splice(j,1);
  1676.       this.extensions=extensions;
  1677.       return true;
  1678.     }
  1679.     return false;
  1680.   }
  1681. ,
  1682.   extensionExists: function(ext,extensions) {
  1683.     return this.indexOfExtension(ext,extensions)>-1;
  1684.   }
  1685. ,
  1686.   indexOfExtension: function(ext, extensions) {
  1687.     var ext = ext.toLowerCase();
  1688.     if(typeof(extensions) != "object") extensions = this.extensions;
  1689.     for(var j=extensions.length; j-->0;) {
  1690.       if(extensions[j].toLowerCase() == ext) return j;
  1691.     }
  1692.     return -1;
  1693.   }
  1694. ,
  1695.   _httpServer: null,
  1696.   get httpServer() {
  1697.     if(typeof(FlashGotHttpServer) != "function") {
  1698.       Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
  1699.           .getService(Components.interfaces.mozIJSSubScriptLoader)
  1700.           .loadSubScript("chrome://flashgot/content/flashgotHttpServer.js", null);
  1701.     }
  1702.     return ((!this._httpServer) || this._httpServer.isDown) ?
  1703.        this._httpServer=new FlashGotHttpServer(this)
  1704.       :this._httpServer;
  1705.   }
  1706.  
  1707. ,
  1708.   download: function(links, opType, dmName) {
  1709.     
  1710.     switch(links.length) {
  1711.       case 0: 
  1712.         return false;
  1713.       case 1: 
  1714.         opType = this.OP_ONE; 
  1715.         break;
  1716.       default:
  1717.         if(!opType) opType = this.OP_SEL;
  1718.     }
  1719.     
  1720.     if(!dmName) dmName=this.defaultDM;
  1721.     const dm=this.DMS[dmName];
  1722.     if(!dm) {
  1723.       this.log("FlashGot error: no download manager selected!");
  1724.       return false;
  1725.     }
  1726.     
  1727.     // surrogate missing attributes
  1728.     
  1729.     if(!links.progress) {
  1730.       links.progress = { update: function() {} };
  1731.     } else {
  1732.       links.progress.update(12);
  1733.     }
  1734.     
  1735.    
  1736.     var service = this;
  1737.     this._delay(function(t) { service._downloadDelayed(links, opType, dm); }); 
  1738.     return true;
  1739.   },
  1740.   
  1741.   _downloadDelayed: function(links, opType, dm) {
  1742.     
  1743.      if(!links.postData) { 
  1744.       links.postData = null;
  1745.     } else if(!dm.postSupported) {
  1746.       // surrogate POST parameters as query string
  1747.       links[0].href += (links[0].href.indexOf("?") > -1 ?  "&" : "?") + links.postData;
  1748.     }
  1749.  
  1750.     const encodedURLs=this.getPref(dm.codeName+".encode",this.getPref("encode",true));
  1751.  
  1752.     const extFilter = this.getPref("extfilter", false) && !this.interceptor.interceptAll ?
  1753.         new RegExp("\.(" +
  1754.           this.extensions.join("|").replace(/[^\w-|]/,"") + 
  1755.           ")\\b", "i") : null;
  1756.     
  1757.     var logMsg = "Processing "+links.length+" links ";
  1758.     if(this.logEnabled && typeof(links.startTime) == "number") {
  1759.       logMsg += "scanned in ms" + (Date.now() - links.startTime);
  1760.     }
  1761.     
  1762.     
  1763.  
  1764.     var startTime = Date.now();
  1765.     const pg=links.progress;
  1766.     
  1767.     const escapeCheckNo=/(%[0-9a-f]{2,4})/i;
  1768.     const escapeCheckYes=/[\s]+/;
  1769.     
  1770.     var len = links.length;
  1771.     
  1772.     var filters = null;
  1773.     if(len > 1) {
  1774.       filters = [];
  1775.       
  1776.       const isValid = dm.isValidLink; 
  1777.       if(isValid)  filters.push(function() { return isValid(href) });
  1778.       
  1779.       if(extFilter) filters.push(function() { return extFilter.test(href) });
  1780.       
  1781.       if(filters.length) {
  1782.         filters.doFilter = function(href) {
  1783.           for(var j = this.length; j-- > 0;) if(!this[j](href)) return false;
  1784.           return true;
  1785.         }
  1786.       } else {
  1787.         filters = null;
  1788.       }
  1789.     }
  1790.  
  1791.     const map = {};
  1792.     
  1793.     var k, j, l, href, ol, pos1, pos2;
  1794.     var jCount;
  1795.     // divide et impera :)
  1796.     var chunkLen = 100;
  1797.     const chunks = Math.ceil(len / chunkLen); 
  1798.     j = 0;
  1799.     for(k = 1; k <= chunks; k++) {
  1800.       if(k == chunks) chunkLen = len % chunkLen;
  1801.       if(j > 0) {
  1802.         pg.update(10 + 30 * j / len);
  1803.       }
  1804.      
  1805.       for(jCount = j + chunkLen; j < jCount; j++) {
  1806.         l = links[j];
  1807.         l._pos = j;
  1808.         href = l.href;
  1809.         if((!filters) || filters.doFilter(href)) {
  1810.           ol = map[href];
  1811.           if(ol) { // duplicate, keep the longest description
  1812.             if(ol.description.length < l.description.length) {
  1813.               map[href] = l;
  1814.               l.href = ol.href; // keep sanitizations
  1815.             }
  1816.           } else {
  1817.             map[href] = l;
  1818.             
  1819.             // encoding checks
  1820.             try {
  1821.               if(encodedURLs) { 
  1822.                 if(escapeCheckYes.test(href) || !escapeCheckNo.test(href)) { 
  1823.                   href = encodeURI(href);
  1824.                 }
  1825.                 // workaround for malformed hash urls
  1826.                 while((pos1 = href.indexOf("#")) > -1 // has fragment?
  1827.                   && href[pos1 + 1] != "!" // skip metalinks!
  1828.                   && (href.indexOf("?") > pos1 || pos1 != href.lastIndexOf('#')) // fragment before query or double fragment ? 
  1829.                 ) {
  1830.                   href = href.substring(0, pos1) + '%23' + href.substring(pos1 + 1);
  1831.                 }
  1832.                 l.href = href;
  1833.               } else {  
  1834.                 l.href = decodeURI(href);
  1835.               }
  1836.             } catch(e) {
  1837.               dump("Problem "
  1838.                 + ( encodedURLs ? "escaping" : "unescaping")
  1839.                 + " URL " + href + ": "+ e.message + "\n");
  1840.             }
  1841.           }
  1842.         }
  1843.       }
  1844.     }
  1845.    
  1846.     links.length = 0;
  1847.     for(href in map) links[links.length] = map[href];
  1848.     
  1849.     if(this.getPref("httpauth", false)) {
  1850.       dm.log("Adding authentication info");
  1851.       this._addAuthInfo(links);
  1852.     }
  1853.     
  1854.     if(dm.supportsMetalink && this.getPref("metalink", true)) {
  1855.       dm.log("Adding metalink info");
  1856.       if(this._processMetalinks(links)) {
  1857.         opType = this.OP_SEL; // force "ask path"
  1858.       }
  1859.     }
  1860.     
  1861.     if(links.length>1) {
  1862.       dm.log("Sorting again "+links.length+" links");
  1863.       links.sort(function(a,b) {
  1864.         a=a._pos; b=b._pos;
  1865.         return a>b?1:a<b?-1:0;
  1866.       });
  1867.     }
  1868.  
  1869.     pg.update(50);
  1870.     
  1871.     dm.log("Preprocessing done in ms" + (Date.now() - startTime) );
  1872.     
  1873.     // "true" download
  1874.     this._delay(function(t) {
  1875.         dm.log("Starting dispatch");
  1876.         var startTime = Date.now();
  1877.     
  1878.         dm.download(links, opType);
  1879.  
  1880.         var now = Date.now();
  1881.         var logMsg = "Dispatch done in ms" + (now - startTime);
  1882.         if(typeof(links.startTime) == "number") { 
  1883.           logMsg += "\nTotal processing time: ms" + (now - links.startTime);
  1884.         }  
  1885.         dm.log(logMsg);
  1886.       });
  1887.   },
  1888.   _addAuthInfo: function(links) {
  1889.     const httpAuthManager = Components.classes['@mozilla.org/network/http-auth-manager;1']
  1890.                               .getService(Components.interfaces.nsIHttpAuthManager);
  1891.     const ioService = Components.classes["@mozilla.org/network/io-service;1"]
  1892.                         .getService(Components.interfaces.nsIIOService);
  1893.     var uri;
  1894.     var udom = {};
  1895.     var uname = {};
  1896.     var upwd = {};
  1897.     var l;
  1898.     for(var j = links.length; j-- > 0;) {
  1899.       l = links[j];
  1900.       try {
  1901.         uri = ioService.newURI(l.href, null, null);
  1902.         if(l.userPass && l.userPass.indexOf(":") > -1) continue;
  1903.         httpAuthManager.getAuthIdentity(uri.scheme, uri.host, uri.port < 0 ? (uri.scheme == "https" ? 443 : 80) : uri.port, null, null, uri.path, udom, uname, upwd);
  1904.         this.log("Authentication data for " + uri + " added.");
  1905.         l.href = uri.scheme + "://" + uname.value + ":" + upwd.value + "@" + 
  1906.                  uri.host + (uri.port < 0 ? "" : (":" + uri.port)) + uri.spec.substring(uri.prePath.length);
  1907.       } catch(e) {}
  1908.     }
  1909.   },
  1910.   _processMetalinks: function(links) {
  1911.     var hasMetalinks = false;
  1912.     var l, k, href, pos, parts, couple, key;
  1913.     for(var j = links.length; j-- > 0;) {
  1914.        l = links[j];
  1915.        href = l.href;
  1916.        pos = href.indexOf("#!");
  1917.        if(pos < 0) continue;
  1918.        parts = href.substring(pos + 2).split("#!");
  1919.        if(parts[0].indexOf("metalink3!") == 0) continue; // per Ant request
  1920.        
  1921.        hasMetalinks = true;
  1922.        l.metalinks = [];
  1923.        for(k = 0; k < parts.length; k++) {
  1924.          couple = parts[k].split("!");
  1925.          if(couple.length != 2) continue;
  1926.          key = couple[0].toLowerCase();
  1927.          switch(key) {
  1928.            case "md5": case "sha1":
  1929.              l[key] = couple[1];
  1930.              break;
  1931.            case "metalink":
  1932.             if(/^(https?|ftp):/i.test(couple[1])) {
  1933.               l.metalinks.push(couple[1]);
  1934.             }
  1935.             break;
  1936.          }
  1937.        }
  1938.     }
  1939.     return links.hasMetalinks = hasMetalinks;
  1940.   }
  1941. ,
  1942.   _delay: function(callback, time) {
  1943.      var timerCallback = { notify: callback }; 
  1944.      Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer)
  1945.               .initWithCallback(timerCallback, time || 0, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
  1946.   }
  1947. ,
  1948.   yield: function() {
  1949.     try {
  1950.       const eqs = Components.interfaces.nsIEventQueueService;
  1951.       if(eqs) {
  1952.         Components.classes["@mozilla.org/event-queue-service;1"]
  1953.           .getService(eqs).getSpecialEventQueue(eqs.UI_THREAD_EVENT_QUEUE)
  1954.           .processPendingEvents();
  1955.       } else {
  1956.         const curThread = Components.classes["@mozilla.org/thread-manager;1"].getService().currentThread;
  1957.         while(curThread.hasPendingEvents()) curThread.processNextEvent(false);
  1958.       }
  1959.     } catch(e) {}
  1960.   },
  1961.   
  1962.   
  1963.   
  1964.   get bgProcessing() {
  1965.     return false;
  1966.       // this.getPref("bgProcessing", true);
  1967.   }
  1968. ,
  1969.   get prefService() {
  1970.     return Components.classes["@mozilla.org/preferences-service;1"].getService(
  1971.       Components.interfaces.nsIPrefService);
  1972.   }
  1973. ,
  1974.   savePrefs: function() {
  1975.     return this.prefService.savePrefFile(null);
  1976.   }
  1977. ,
  1978.   getPref: function(name,def) {
  1979.     const IPC=Components.interfaces.nsIPrefBranch;
  1980.     const prefs=this.prefs;
  1981.     try {
  1982.       switch(prefs.getPrefType(name)) {
  1983.         case IPC.PREF_STRING:
  1984.           return prefs.getCharPref(name);
  1985.         case IPC.PREF_INT:
  1986.           return prefs.getIntPref(name);
  1987.         case IPC.PREF_BOOL:
  1988.           return prefs.getBoolPref(name);
  1989.       }
  1990.     } catch(e) {}
  1991.     return def;
  1992.   }
  1993. ,
  1994.   setPref: function(name,value) {
  1995.     const prefs=this.prefs;
  1996.     switch(typeof(value)) {
  1997.       case "string":
  1998.           prefs.setCharPref(name,value);
  1999.           break;
  2000.       case "boolean":
  2001.         prefs.setBoolPref(name,value);
  2002.         break;
  2003.       case "number":
  2004.         prefs.setIntPref(name,value);
  2005.         break;
  2006.       default:
  2007.         throw new Error("Unsupported type "+typeof(value)+" for preference "+name);
  2008.     }
  2009.   }
  2010. ,
  2011.   _bundle: null,
  2012.   get bundle() {
  2013.     if(!this._bundle) {
  2014.       function getBundle(url) {
  2015.         try {
  2016.           var bundle = bs.createBundle(url);
  2017.           bundle.GetStringFromName("flashgot");
  2018.         } catch(ex) {
  2019.           dump("\n"+ex+"\n");
  2020.           bundle = null;
  2021.         }
  2022.         return bundle;
  2023.       }
  2024.       
  2025.       var bs = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(
  2026.         Components.interfaces.nsIStringBundleService);
  2027.       if(! ( 
  2028.             (this._bundle=getBundle("chrome://flashgot/locale/flashgot.properties") )
  2029.             || (this._bundle=bs.createBundle("chrome://flashgot/content/en-US/flashgot.properties"))
  2030.             )
  2031.         ) {
  2032.         this._bundle = {
  2033.           formatStringFromName: function(name,parms) {
  2034.             return name+" ["+parms.join(',')+"]"
  2035.           },
  2036.           GetStringFromName: function(name) {
  2037.             return name;
  2038.           }
  2039.         };
  2040.       }
  2041.     }
  2042.     return this._bundle;
  2043.   }
  2044. ,
  2045.   getString: function(name,parms) {
  2046.     const bundle=this.bundle;
  2047.     try {
  2048.       return (parms
  2049.           ?bundle.formatStringFromName(name,parms,parms.length)
  2050.           :bundle.GetStringFromName(name));
  2051.     } catch(ex) {
  2052.       return "???";
  2053.     }
  2054.   }
  2055. ,
  2056.   _logFile: null,
  2057.   get logFile() {
  2058.     if(this._logFile==null) {
  2059.       this._logFile=this.profDir.clone();
  2060.       this._logFile.append("flashgot.log");
  2061.     }
  2062.     return this._logFile;
  2063.   }
  2064. ,
  2065.   logStream: null,
  2066.   logEnabled: false,
  2067.   log: function(msg) {
  2068.     if(this.logEnabled) {
  2069.       try {
  2070.         if(!this.logStream) {
  2071.           const logFile=this.logFile;
  2072.           const logStream=Components.classes["@mozilla.org/network/file-output-stream;1"
  2073.             ].createInstance(Components.interfaces.nsIFileOutputStream );
  2074.           logStream.init(logFile, 0x02 | 0x08 | 0x10, 0600, 0 );
  2075.           this.logStream=logStream;
  2076.           const header="*** Log start at "+new Date().toGMTString()+"\n";
  2077.           this.logStream.write(header,header.length);
  2078.         }
  2079.         
  2080.         if(msg!=null) {
  2081.           msg+="\n";
  2082.           this.logStream.write(msg,msg.length);
  2083.         }
  2084.         this.logStream.flush();
  2085.       } catch(ex) {
  2086.         dump(ex.message+"\noccurred logging this message:\n"+msg);
  2087.       }
  2088.     }
  2089.   }
  2090. ,
  2091.   dumpStack: function(msg) {
  2092.     dump( (msg?msg:"")+"\n"+new Error().stack+"\n");
  2093.   }
  2094. ,
  2095.   clearLog: function() {
  2096.     try {
  2097.       if(this.logStream) {
  2098.         try {
  2099.           this.logStream.close();
  2100.         } catch(eexx) {
  2101.           dump(eexx.message);
  2102.         }
  2103.       }
  2104.       if(this.logFile) this.logFile.remove(true);
  2105.       this.logStream=null;
  2106.       this.log(null);
  2107.     } catch(ex) { dump(ex.message); }
  2108.   } 
  2109. ,
  2110.   get windowMediator() {
  2111.     return Components.classes["@mozilla.org/appshell/window-mediator;1"
  2112.       ].getService(Components.interfaces.nsIWindowMediator);
  2113.   }
  2114. ,
  2115.   getWindow: function() {
  2116.     return this.windowMediator.getMostRecentWindow(null);
  2117.   }
  2118. ,
  2119.   _globals: null,
  2120.   get globals() {
  2121.     if(!this._initialized) {
  2122.       this.initGlobals();
  2123.     }
  2124.     return this._globals;
  2125.   }
  2126. ,
  2127.   PREFS_BRANCH: "flashgot."
  2128. ,
  2129.   _prefs: null,
  2130.   get prefs() {
  2131.     var prefs=this._prefs;
  2132.     if(!prefs) {
  2133.       this._prefs=prefs=this.prefService.getBranch(this.PREFS_BRANCH
  2134.         ).QueryInterface(Components.interfaces.nsIPrefBranchInternal);
  2135.     }
  2136.     return prefs;
  2137.   }
  2138. ,
  2139.   _initialized: false,
  2140.   initGlobals: function() {
  2141.     if(this._globals || this._initialized) return;
  2142.     
  2143.     function prepareTmp(t) {
  2144.       t.append("flashgot."+encodeURI(profDir.leafName).replace(/%/g,"_"));
  2145.       if(t.exists()) {
  2146.        if(!t.isDirectory()) t.createUnique(1,0700);
  2147.       } else {
  2148.         t.create(1,0700);
  2149.       }
  2150.       return t;
  2151.     }
  2152.     
  2153.     try {
  2154.       const startTime = Date.now();
  2155.       const prefs=this.prefs;
  2156.       const cc=Components.classes;
  2157.       const ci=Components.interfaces; 
  2158.  
  2159.       const fileLocator=cc["@mozilla.org/file/directory_service;1"].getService(
  2160.         ci.nsIProperties);
  2161.       const profDir=fileLocator.get("ProfD",ci.nsIFile);
  2162.      
  2163.       var tmpDir;
  2164.       try {
  2165.         tmpDir=prepareTmp(prefs.getComplexValue("tmpDir", ci.nsILocalFile));
  2166.       } catch(ex) {
  2167.         tmpDir=prepareTmp(fileLocator.get("TmpD", ci.nsILocalFile));
  2168.       }
  2169.        
  2170.       this._globals={
  2171.         tmpDir: tmpDir,
  2172.         profDir: profDir,
  2173.         prefs: prefs
  2174.       };
  2175.       
  2176.       prefs.addObserver("", this, false);
  2177.       this.syncPrefs();
  2178.       
  2179.       this.log("Per-session init started");
  2180.         
  2181.       this._setupLegacyPrefs();
  2182.  
  2183.       this._globals.DMS=this.checkDownloadManagers(true, false);
  2184.       this.log("Per-session init done in " + (Date.now() - startTime) + "ms");
  2185.     } catch(initEx) {
  2186.       this._initException=initEx;
  2187.     }
  2188.     this._initialized=true; 
  2189.   }
  2190. ,
  2191.   dispose: function() {
  2192.     this.prefs.removeObserver("",this);
  2193.     this._prefs=null;
  2194.     this._initialized=false;
  2195.     this._globals=null;
  2196.   }
  2197. ,
  2198.   createCustomDM: function(name) {
  2199.     const dm = new FlashGotDMCust(name);
  2200.     if(name && name.length) {
  2201.       FlashGotDMCust.persist(this);
  2202.       this.sortDMS();
  2203.       this.checkDownloadManagers(false, false);
  2204.     }
  2205.     return dm;
  2206.   }
  2207. ,
  2208.  removeCustomDM: function(name) {
  2209.    const dms = FlashGotDM.dms;
  2210.    for(var j = dms.length; j-->0;) {
  2211.      if(dms[j].custom && dms[j].name == name) {
  2212.        dms.splice(j, 1);
  2213.        delete dms[name];
  2214.      }
  2215.    }
  2216.    FlashGotDMCust.persist(this);
  2217.    this.checkDownloadManagers(false, false);
  2218.  }
  2219. ,
  2220.   sortDMS: function() {
  2221.     FlashGotDM.dms.sort(function(a,b) { a=a.name.toLowerCase(); b=b.name.toLowerCase(); return a>b?1:a<b?-1:0; });
  2222.   }
  2223.   checkDownloadManagers: function(init, detect) {
  2224.     
  2225.     if(init || detect) {
  2226.       FlashGotDM.init(this);
  2227.     }
  2228.     
  2229.     const dms = FlashGotDM.dms;
  2230.     dms.found = false;
  2231.     var defaultDM = this.defaultDM;
  2232.     if(!dms[defaultDM]) defaultDM = null;
  2233.     
  2234.     detect = detect || this.getPref("detect.auto", true);
  2235.  
  2236.     var j, dm;
  2237.     var cache;
  2238.     
  2239.     if(!detect) {
  2240.       cache = this.getPref("detect.cache", "").split(",");
  2241.       for(j = dms.length; j-- > 0;) {
  2242.         dm = dms[j];
  2243.         if(!dm.custom) dm._supported = false;
  2244.       }
  2245.       var name;
  2246.       for(j = cache.length; j-- > 0;) {
  2247.         name = cache[j];
  2248.         if(name.length && typeof(dm = dms[name])=="object" && dm.name == name) {
  2249.           dm._supported = true;
  2250.         }
  2251.       }
  2252.     }
  2253.     
  2254.     cache = [];
  2255.     var exclusive;
  2256.     var firstSupported=null;
  2257.     for(j=dms.length; j-- >0;) {
  2258.       dm=dms[j];
  2259.       if(dm.supported) {
  2260.         dms.found = true;
  2261.         cache[cache.length] = firstSupported = dm.name;
  2262.         if(dm.exclusive) exclusive=true;
  2263.       } else {
  2264.         this.log("Warning: download manager "+dm.name+" not found");
  2265.         if(defaultDM==dm.name) {
  2266.           defaultDM=null;
  2267.           this.log(dm.name+" was default download manager: resetting.");
  2268.         }
  2269.       }
  2270.     }
  2271.     
  2272.     this.setPref("detect.cache",cache.join(","));
  2273.     
  2274.     if( (!defaultDM) && firstSupported!=null) {
  2275.       this.defaultDM=firstSupported;
  2276.       this.log("Default download manager set to "+this.defaultDM);
  2277.     } else if(!dms.found) {
  2278.       this.log("Serious warning! no supported download manager found...");
  2279.     } 
  2280.     if(exclusive) {
  2281.       for(j=dms.length; j-->0;) {
  2282.         if(! (dms[j].custom || dms[j].supported) ) {
  2283.           dms.splice(j,1);
  2284.         }
  2285.       }
  2286.     }
  2287.     
  2288.     return dms;
  2289.   }
  2290. ,
  2291.   _referrerSpoofer: null,
  2292.   get referrerSpoofer() {
  2293.     if(typeof(ReferrerSpoofer) != "function") {
  2294.       Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
  2295.           .getService(Components.interfaces.mozIJSSubScriptLoader)
  2296.           .loadSubScript("chrome://flashgot/content/referrerSpoofer.js", null);
  2297.     }
  2298.     return (!this._httpServer) ? this._referrerSpoofer = new ReferrerSpoofer() :this._referrerSpoofer;
  2299.   }
  2300. ,
  2301.   _cleaningup: false
  2302. ,
  2303.   cleanup: function() {
  2304.     if(this._cleaningup) return;
  2305.     try {
  2306.       this._cleaningup=true;
  2307.       this.log("Starting cleanup");
  2308.       if(this._httpServer) {
  2309.         this._httpServer.shutdown();
  2310.       }
  2311.       
  2312.       try {
  2313.         FlashGotDM.cleanup();
  2314.       } catch(eexx) {
  2315.         dump(eexx.message);
  2316.       }
  2317.       
  2318.       if(this._globals && this._globals.tmpDir.exists()) {
  2319.         try {
  2320.           this._globals.tmpDir.remove(true);
  2321.         } catch(eexx) {
  2322.           this.log("Can't remove "+this._globals.tmpDir.path+", maybe still in use: "+eexx);
  2323.         }
  2324.       }
  2325.       this._bundle=null;
  2326.       this.log("Cleanup done");
  2327.       if(this._logFile) try {
  2328.         if(this.logStream) this.logStream.close();
  2329.         var maxLogSize=Math.max(Math.min(this.getPref('maxLogSize',100000),1000000),50000);
  2330.         const logFile=this.logFile;
  2331.         const logSize=logFile.fileSize;
  2332.         if(logSize>maxLogSize) { // log rotation
  2333.           dump("Cutting log (size: "+logSize+", max: "+maxLogSize+")");
  2334.           const cc=Components.classes;
  2335.           const ci=Components.interfaces;
  2336.          
  2337.           const logBak=logFile.clone();
  2338.           logBak.leafName=logBak.leafName+".bak";
  2339.           if(logBak.exists()) logBak.remove(true);
  2340.           logFile.copyTo(logBak.parent,logBak.leafName);
  2341.           const is=cc['@mozilla.org/network/file-input-stream;1'].createInstance(
  2342.             ci.nsIFileInputStream);
  2343.           is.init(logBak,0x01, 0400, null);
  2344.           is.QueryInterface(ci.nsISeekableStream);
  2345.           is.seek(ci.nsISeekableStream.NS_SEEK_END,-maxLogSize);
  2346.           const sis=cc['@mozilla.org/scriptableinputstream;1'].createInstance(
  2347.           ci.nsIScriptableInputStream);
  2348.           sis.init(is);
  2349.           var buffer;
  2350.           var content="\n";
  2351.           var logStart=-1;
  2352.           while(buffer=sis.read(5000)) {
  2353.             content+=buffer;
  2354.             if((logStart=content.indexOf("\n*** Log start at "))>-1) { 
  2355.               content=content.substring(logStart);
  2356.               break;
  2357.             }
  2358.             content=buffer;
  2359.           }
  2360.           if(logStart>-1) {
  2361.              const os=cc["@mozilla.org/network/file-output-stream;1"].createInstance(
  2362.               ci.nsIFileOutputStream);
  2363.             os.init(logFile,0x02 | 0x08 | 0x20, 0700, 0);
  2364.             os.write(content,content.length);
  2365.             while(buffer=sis.read(20000)) {
  2366.               os.write(buffer,buffer.length);
  2367.             } 
  2368.             os.close();
  2369.           }
  2370.           sis.close();
  2371.         }
  2372.       } catch(eexx) {
  2373.         dump("Error cleaning up log: "+eexx);
  2374.       }
  2375.       this.logStream=null;
  2376.     } catch(ex) {
  2377.        this.log(ex);
  2378.     }
  2379.     this._cleaningup=false;
  2380.     this.dispose();
  2381.   }
  2382. ,
  2383.   readFile: function(file) {
  2384.     const cc=Components.classes;
  2385.     const ci=Components.interfaces; 
  2386.     
  2387.     const is = cc["@mozilla.org/network/file-input-stream;1"].createInstance(
  2388.           ci.nsIFileInputStream );
  2389.     is.init(file ,0x01, 0400, null);
  2390.     const sis = cc["@mozilla.org/scriptableinputstream;1"].createInstance(
  2391.       ci.nsIScriptableInputStream );
  2392.     sis.init(is);
  2393.     const res=sis.read(sis.available());
  2394.     is.close();
  2395.     return res;
  2396.   }
  2397. ,
  2398.   writeFile: function(file, content, charset) {
  2399.     const cc=Components.classes;
  2400.     const ci=Components.interfaces;
  2401.     const unicodeConverter = cc["@mozilla.org/intl/scriptableunicodeconverter"].createInstance(
  2402.     ci.nsIScriptableUnicodeConverter);
  2403.     try {
  2404.       unicodeConverter.charset = charset?charset:"UTF-8";
  2405.     } catch(ex) {
  2406.       unicodeConverter.charset = "UTF-8";
  2407.     }
  2408.     content=unicodeConverter.ConvertFromUnicode(content);
  2409.     const os=cc["@mozilla.org/network/file-output-stream;1"].createInstance(
  2410.       ci.nsIFileOutputStream);
  2411.     os.init(file,0x02,0700,0);
  2412.     os.write(content,content.length);
  2413.     os.close();
  2414.   }
  2415. ,
  2416.   _lookupMethod: null,
  2417.   get lookupMethod() {
  2418.     return this._lookupMethod?this._lookupMethod:(this._lookupMethod = 
  2419.       (Components.utils && Components.utils.lookupMethod)
  2420.         ?Components.utils.lookupMethod:Components.lookupMethod);
  2421.   }
  2422. ,
  2423.   _setupLegacyPrefs: function() {
  2424.     try {
  2425.       const file=this._globals.profDir.clone();
  2426.       const defFile=file.clone();
  2427.       file.append("pref");
  2428.       file.append("flashgot.js");
  2429.       defFile.append("prefs.js");
  2430.       if(file.exists() && defFile.exists()) {
  2431.         this.prefService.readUserPrefs(file);
  2432.         this.prefService.readUserPrefs(defFile);
  2433.         this.savePrefs();
  2434.         file.remove(true);
  2435.       }
  2436.     } catch(e) {
  2437.       this.log(e.message);
  2438.     }
  2439.   }
  2440. ,
  2441.   showDMSReference: function() {
  2442.     this.getWindow().open("http://www.flashgot.net/dms","_blank");
  2443.   }
  2444.   dirtyJobsDone: false
  2445. }
  2446.  
  2447. // XPCOM Scaffolding code
  2448.  
  2449. // component defined in this file
  2450.  
  2451. const SERVICE_NAME="FlashGot Service";
  2452. const SERVICE_CID =
  2453.     Components.ID("{2a55fc5c-7b31-4ee1-ab15-5ee2eb428cbe}");
  2454. const SERVICE_CTRID =
  2455.     "@maone.net/flashgot-service;1";
  2456.     
  2457. const SERVICE_CONSTRUCTOR=FlashGotService;
  2458. const SERVICE_FLAGS = 3; // SINGLETON | THREADSAFE
  2459.  
  2460. // interfaces implemented by this component
  2461. const SERVICE_IIDS = 
  2462. Components.interfaces.nsISupports,
  2463. Components.interfaces.nsISupportsWeakReference,
  2464. Components.interfaces.nsIClassInfo,
  2465. Components.interfaces.nsIObserver,
  2466. Components.interfaces.nsIURIContentListener
  2467. ];
  2468.  
  2469. // Factory object
  2470. const SERVICE_FACTORY = {
  2471.   _instance: null,
  2472.   createInstance: function (outer, iid) {
  2473.     if (outer != null)
  2474.         throw Components.results.NS_ERROR_NO_AGGREGATION;
  2475.  
  2476.     xpcom_checkInterfaces(iid,SERVICE_IIDS,Components.results.NS_ERROR_INVALID_ARG);
  2477.     // kept this for flexibility sake, but we're really adopting an
  2478.     // early instantiation and late init singleton pattern
  2479.     return this._instance==null?this._instance=this._create():this._instance;
  2480.   },
  2481.   _create: function() {
  2482.     var obj=new SERVICE_CONSTRUCTOR();
  2483.     obj.__defineGetter__("classDescription",function() { return SERVICE_NAME; });
  2484.     obj.__defineGetter__("classID",function() { return SERVICE_CID; });
  2485.     obj.__defineGetter__("classIDNoAlloc",function() { return SERVICE_CTRID; });
  2486.     obj.__defineGetter__("contractID",function() { return SERVICE_CTRID; });
  2487.     obj.__defineGetter__("flags",function() { return SERVICE_FLAGS; });
  2488.     obj.__defineGetter__("implementationLanguage",function() { return 2; });
  2489.     obj.getHelperForLanguage = function() { return null; };
  2490.     obj.getInterfaces = function(count) { 
  2491.       count.value = 0; 
  2492.       return null; 
  2493.     };
  2494.     return obj;
  2495.   }
  2496. };
  2497.  
  2498. function xpcom_checkInterfaces(iid,iids,ex) {
  2499.   for(var j=iids.length; j-- >0;) {
  2500.     if(iid.equals(iids[j])) return true;
  2501.   }
  2502.   throw ex;
  2503. }
  2504.  
  2505. // Module
  2506.  
  2507. var Module = new Object();
  2508. Module.firstTime=true;
  2509. Module.registerSelf = function (compMgr, fileSpec, location, type) {
  2510.   if(this.firstTime) {
  2511.    
  2512.     debug("*** Registering "+SERVICE_CTRID+".\n");
  2513.     
  2514.     compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar
  2515.       ).registerFactoryLocation(SERVICE_CID,
  2516.       SERVICE_NAME,
  2517.       SERVICE_CTRID, 
  2518.       fileSpec,
  2519.       location, 
  2520.       type);
  2521.       
  2522.     Components.classes['@mozilla.org/categorymanager;1'].getService(
  2523.       Components.interfaces.nsICategoryManager
  2524.      ).addCategoryEntry("app-startup",
  2525.         SERVICE_NAME, "service," + SERVICE_CTRID, true, true, null);
  2526.       
  2527.     this.firstTime=false;
  2528.   } 
  2529. }
  2530. Module.unregisterSelf = function(compMgr, fileSpec, location) {
  2531.   compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar
  2532.     ).unregisterFactoryLocation(SERVICE_CID, fileSpec);
  2533.   Components.classes['@mozilla.org/categorymanager;1'].getService(
  2534.       Components.interfaces.nsICategoryManager
  2535.      ).deleteCategoryEntry("app-startup",SERVICE_NAME, true);
  2536. }
  2537.  
  2538. Module.getClassObject = function (compMgr, cid, iid) {
  2539.   if(cid.equals(SERVICE_CID))
  2540.     return SERVICE_FACTORY;
  2541.  
  2542.   if (!iid.equals(Components.interfaces.nsIFactory))
  2543.     throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  2544.   
  2545.   throw Components.results.NS_ERROR_NO_INTERFACE;
  2546.     
  2547. }
  2548.  
  2549. Module.canUnload = function(compMgr) {
  2550.   return true;
  2551. }
  2552.  
  2553. // entrypoint
  2554. function NSGetModule(compMgr, fileSpec) {
  2555.   return Module;
  2556. }
  2557.  
  2558.